@terreno/api 0.0.11-beta.1 → 0.0.12

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/api.test.js CHANGED
@@ -89,493 +89,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
89
89
  };
90
90
  Object.defineProperty(exports, "__esModule", { value: true });
91
91
  var bun_test_1 = require("bun:test");
92
- var Sentry = __importStar(require("@sentry/node"));
93
- var qs_1 = __importDefault(require("qs"));
94
92
  var supertest_1 = __importDefault(require("supertest"));
95
93
  var api_1 = require("./api");
96
94
  var auth_1 = require("./auth");
97
- var errors_1 = require("./errors");
98
- var expressServer_1 = require("./expressServer");
99
95
  var permissions_1 = require("./permissions");
100
96
  var tests_1 = require("./tests");
101
97
  var transformers_1 = require("./transformers");
102
98
  (0, bun_test_1.describe)("@terreno/api", function () {
103
99
  var server;
104
100
  var app;
105
- (0, bun_test_1.describe)("pre and post hooks", function () {
101
+ (0, bun_test_1.describe)("populate", function () {
102
+ var admin;
103
+ var notAdmin;
106
104
  var agent;
105
+ var spinach;
107
106
  (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
108
- return __generator(this, function (_a) {
109
- switch (_a.label) {
107
+ var _a, _b;
108
+ return __generator(this, function (_c) {
109
+ switch (_c.label) {
110
110
  case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
111
111
  case 1:
112
- _a.sent();
113
- app = (0, tests_1.getBaseServer)();
114
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
115
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
116
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
117
- case 2:
118
- agent = _a.sent();
119
- return [2 /*return*/];
120
- }
121
- });
122
- }); });
123
- (0, bun_test_1.it)("pre hooks change data", function () { return __awaiter(void 0, void 0, void 0, function () {
124
- var deleteCalled, res, broccoli;
125
- return __generator(this, function (_a) {
126
- switch (_a.label) {
127
- case 0:
128
- deleteCalled = false;
129
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
130
- allowAnonymous: true,
131
- permissions: {
132
- create: [permissions_1.Permissions.IsAny],
133
- delete: [permissions_1.Permissions.IsAny],
134
- list: [permissions_1.Permissions.IsAny],
135
- read: [permissions_1.Permissions.IsAny],
136
- update: [permissions_1.Permissions.IsAny],
137
- },
138
- preCreate: function (data) {
139
- data.calories = 14;
140
- return data;
141
- },
142
- preDelete: function (data) {
143
- deleteCalled = true;
144
- return data;
145
- },
146
- preUpdate: function (data) {
147
- data.calories = 15;
148
- return data;
149
- },
150
- }));
151
- server = (0, supertest_1.default)(app);
152
- return [4 /*yield*/, server
153
- .post("/food")
154
- .send({
155
- calories: 15,
156
- name: "Broccoli",
157
- })
158
- .expect(201)];
159
- case 1:
160
- res = _a.sent();
161
- return [4 /*yield*/, tests_1.FoodModel.findById(res.body.data._id)];
162
- case 2:
163
- broccoli = _a.sent();
164
- if (!broccoli) {
165
- throw new Error("Broccoli was not created");
166
- }
167
- (0, bun_test_1.expect)(broccoli.name).toBe("Broccoli");
168
- // Overwritten by the pre create hook
169
- (0, bun_test_1.expect)(broccoli.calories).toBe(14);
170
- return [4 /*yield*/, server
171
- .patch("/food/".concat(broccoli._id))
172
- .send({
173
- name: "Broccoli2",
174
- })
175
- .expect(200)];
176
- case 3:
177
- res = _a.sent();
178
- (0, bun_test_1.expect)(res.body.data.name).toBe("Broccoli2");
179
- // Updated by the pre update hook
180
- (0, bun_test_1.expect)(res.body.data.calories).toBe(15);
181
- return [4 /*yield*/, agent.delete("/food/".concat(broccoli._id)).expect(204)];
182
- case 4:
183
- _a.sent();
184
- (0, bun_test_1.expect)(deleteCalled).toBe(true);
185
- return [2 /*return*/];
186
- }
187
- });
188
- }); });
189
- (0, bun_test_1.it)("pre hooks return null", function () { return __awaiter(void 0, void 0, void 0, function () {
190
- var notAdmin, spinach, res, broccoli;
191
- return __generator(this, function (_a) {
192
- switch (_a.label) {
193
- case 0: return [4 /*yield*/, tests_1.UserModel.findOne({
194
- email: "notAdmin@example.com",
195
- })];
196
- case 1:
197
- notAdmin = _a.sent();
198
- return [4 /*yield*/, tests_1.FoodModel.create({
199
- calories: 1,
200
- created: new Date("2021-12-03T00:00:20.000Z"),
201
- hidden: false,
202
- name: "Spinach",
203
- ownerId: notAdmin._id,
204
- source: {
205
- name: "Brand",
206
- },
207
- })];
208
- case 2:
209
- spinach = _a.sent();
210
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
211
- allowAnonymous: true,
212
- permissions: {
213
- create: [permissions_1.Permissions.IsAny],
214
- delete: [permissions_1.Permissions.IsAny],
215
- list: [permissions_1.Permissions.IsAny],
216
- read: [permissions_1.Permissions.IsAny],
217
- update: [permissions_1.Permissions.IsAny],
218
- },
219
- preCreate: function () { return null; },
220
- preDelete: function () { return null; },
221
- preUpdate: function () { return null; },
222
- }));
223
- server = (0, supertest_1.default)(app);
224
- return [4 /*yield*/, server
225
- .post("/food")
226
- .send({
227
- calories: 15,
228
- name: "Broccoli",
229
- })
230
- .expect(403)];
231
- case 3:
232
- res = _a.sent();
233
- return [4 /*yield*/, tests_1.FoodModel.findById(res.body._id)];
234
- case 4:
235
- broccoli = _a.sent();
236
- (0, bun_test_1.expect)(broccoli).toBeNull();
237
- return [4 /*yield*/, server
238
- .patch("/food/".concat(spinach._id))
239
- .send({
240
- name: "Broccoli",
241
- })
242
- .expect(403)];
243
- case 5:
244
- _a.sent();
245
- return [4 /*yield*/, server.delete("/food/".concat(spinach._id)).expect(403)];
246
- case 6:
247
- _a.sent();
248
- return [2 /*return*/];
249
- }
250
- });
251
- }); });
252
- (0, bun_test_1.it)("post hooks succeed", function () { return __awaiter(void 0, void 0, void 0, function () {
253
- var deleteCalled, res, broccoli;
254
- return __generator(this, function (_a) {
255
- switch (_a.label) {
256
- case 0:
257
- deleteCalled = false;
258
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
259
- allowAnonymous: true,
260
- permissions: {
261
- create: [permissions_1.Permissions.IsAny],
262
- delete: [permissions_1.Permissions.IsAny],
263
- list: [permissions_1.Permissions.IsAny],
264
- read: [permissions_1.Permissions.IsAny],
265
- update: [permissions_1.Permissions.IsAny],
266
- },
267
- postCreate: function (data) { return __awaiter(void 0, void 0, void 0, function () {
268
- return __generator(this, function (_a) {
269
- switch (_a.label) {
270
- case 0:
271
- data.calories = 14;
272
- return [4 /*yield*/, data.save()];
273
- case 1:
274
- _a.sent();
275
- return [2 /*return*/, data];
276
- }
277
- });
278
- }); },
279
- postDelete: function (data) {
280
- deleteCalled = true;
281
- return data;
282
- },
283
- postUpdate: function (data) { return __awaiter(void 0, void 0, void 0, function () {
284
- return __generator(this, function (_a) {
285
- switch (_a.label) {
286
- case 0:
287
- data.calories = 15;
288
- return [4 /*yield*/, data.save()];
289
- case 1:
290
- _a.sent();
291
- return [2 /*return*/, data];
292
- }
293
- });
294
- }); },
295
- }));
296
- server = (0, supertest_1.default)(app);
297
- return [4 /*yield*/, server
298
- .post("/food")
299
- .send({
300
- calories: 15,
301
- name: "Broccoli",
302
- })
303
- .expect(201)];
304
- case 1:
305
- res = _a.sent();
306
- return [4 /*yield*/, tests_1.FoodModel.findById(res.body.data._id)];
307
- case 2:
308
- broccoli = _a.sent();
309
- if (!broccoli) {
310
- throw new Error("Broccoli was not created");
311
- }
312
- (0, bun_test_1.expect)(broccoli.name).toBe("Broccoli");
313
- // Overwritten by the pre create hook
314
- (0, bun_test_1.expect)(broccoli.calories).toBe(14);
315
- return [4 /*yield*/, server
316
- .patch("/food/".concat(broccoli._id))
317
- .send({
318
- name: "Broccoli2",
319
- })
320
- .expect(200)];
321
- case 3:
322
- res = _a.sent();
323
- return [4 /*yield*/, tests_1.FoodModel.findById(res.body.data._id)];
324
- case 4:
325
- broccoli = _a.sent();
326
- if (!broccoli) {
327
- throw new Error("Broccoli was not update");
328
- }
329
- (0, bun_test_1.expect)(broccoli.name).toBe("Broccoli2");
330
- // Updated by the post update hook
331
- (0, bun_test_1.expect)(broccoli.calories).toBe(15);
332
- return [4 /*yield*/, agent.delete("/food/".concat(broccoli._id)).expect(204)];
333
- case 5:
334
- _a.sent();
335
- (0, bun_test_1.expect)(deleteCalled).toBe(true);
336
- return [2 /*return*/];
337
- }
338
- });
339
- }); });
340
- (0, bun_test_1.it)("preCreate hook preserves disableExternalErrorTracking on APIError", function () { return __awaiter(void 0, void 0, void 0, function () {
341
- var res;
342
- return __generator(this, function (_a) {
343
- switch (_a.label) {
344
- case 0:
345
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
346
- allowAnonymous: true,
347
- permissions: {
348
- create: [permissions_1.Permissions.IsAny],
349
- delete: [permissions_1.Permissions.IsAny],
350
- list: [permissions_1.Permissions.IsAny],
351
- read: [permissions_1.Permissions.IsAny],
352
- update: [permissions_1.Permissions.IsAny],
353
- },
354
- preCreate: function () {
355
- throw new errors_1.APIError({
356
- disableExternalErrorTracking: true,
357
- status: 400,
358
- title: "Custom preCreate error",
359
- });
360
- },
361
- }));
362
- server = (0, supertest_1.default)(app);
363
- return [4 /*yield*/, server
364
- .post("/food")
365
- .send({
366
- calories: 15,
367
- name: "Broccoli",
368
- })
369
- .expect(400)];
370
- case 1:
371
- res = _a.sent();
372
- (0, bun_test_1.expect)(res.body.title).toBe("Custom preCreate error");
373
- (0, bun_test_1.expect)(res.body.disableExternalErrorTracking).toBe(true);
374
- return [2 /*return*/];
375
- }
376
- });
377
- }); });
378
- (0, bun_test_1.it)("preCreate hook preserves disableExternalErrorTracking on non-APIError", function () { return __awaiter(void 0, void 0, void 0, function () {
379
- var res;
380
- return __generator(this, function (_a) {
381
- switch (_a.label) {
382
- case 0:
383
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
384
- allowAnonymous: true,
385
- permissions: {
386
- create: [permissions_1.Permissions.IsAny],
387
- delete: [permissions_1.Permissions.IsAny],
388
- list: [permissions_1.Permissions.IsAny],
389
- read: [permissions_1.Permissions.IsAny],
390
- update: [permissions_1.Permissions.IsAny],
391
- },
392
- preCreate: function () {
393
- var error = new Error("Some custom error");
394
- error.disableExternalErrorTracking = true;
395
- throw error;
396
- },
397
- }));
398
- server = (0, supertest_1.default)(app);
399
- return [4 /*yield*/, server
400
- .post("/food")
401
- .send({
402
- calories: 15,
403
- name: "Broccoli",
404
- })
405
- .expect(400)];
406
- case 1:
407
- res = _a.sent();
408
- (0, bun_test_1.expect)(res.body.title).toContain("preCreate hook error");
409
- (0, bun_test_1.expect)(res.body.disableExternalErrorTracking).toBe(true);
410
- return [2 /*return*/];
411
- }
412
- });
413
- }); });
414
- (0, bun_test_1.it)("preUpdate hook preserves disableExternalErrorTracking on APIError", function () { return __awaiter(void 0, void 0, void 0, function () {
415
- var notAdmin, spinach, res;
416
- return __generator(this, function (_a) {
417
- switch (_a.label) {
418
- case 0: return [4 /*yield*/, tests_1.UserModel.findOne({
419
- email: "notAdmin@example.com",
420
- })];
421
- case 1:
422
- notAdmin = _a.sent();
423
- return [4 /*yield*/, tests_1.FoodModel.create({
424
- calories: 1,
425
- created: new Date("2021-12-03T00:00:20.000Z"),
426
- hidden: false,
427
- name: "Spinach",
428
- ownerId: notAdmin._id,
429
- source: {
430
- name: "Brand",
431
- },
432
- })];
433
- case 2:
434
- spinach = _a.sent();
435
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
436
- allowAnonymous: true,
437
- permissions: {
438
- create: [permissions_1.Permissions.IsAny],
439
- delete: [permissions_1.Permissions.IsAny],
440
- list: [permissions_1.Permissions.IsAny],
441
- read: [permissions_1.Permissions.IsAny],
442
- update: [permissions_1.Permissions.IsAny],
443
- },
444
- preUpdate: function () {
445
- throw new errors_1.APIError({
446
- disableExternalErrorTracking: true,
447
- status: 400,
448
- title: "Custom preUpdate error",
449
- });
450
- },
451
- }));
452
- server = (0, supertest_1.default)(app);
453
- return [4 /*yield*/, server
454
- .patch("/food/".concat(spinach._id))
455
- .send({
456
- name: "Broccoli",
457
- })
458
- .expect(400)];
459
- case 3:
460
- res = _a.sent();
461
- (0, bun_test_1.expect)(res.body.title).toBe("Custom preUpdate error");
462
- (0, bun_test_1.expect)(res.body.disableExternalErrorTracking).toBe(true);
463
- return [2 /*return*/];
464
- }
465
- });
466
- }); });
467
- (0, bun_test_1.it)("preUpdate hook preserves disableExternalErrorTracking on non-APIError", function () { return __awaiter(void 0, void 0, void 0, function () {
468
- var notAdmin, spinach, res;
469
- return __generator(this, function (_a) {
470
- switch (_a.label) {
471
- case 0: return [4 /*yield*/, tests_1.UserModel.findOne({
472
- email: "notAdmin@example.com",
473
- })];
474
- case 1:
475
- notAdmin = _a.sent();
476
- return [4 /*yield*/, tests_1.FoodModel.create({
477
- calories: 1,
478
- created: new Date("2021-12-03T00:00:20.000Z"),
479
- hidden: false,
480
- name: "Spinach",
481
- ownerId: notAdmin._id,
482
- source: {
483
- name: "Brand",
484
- },
485
- })];
486
- case 2:
487
- spinach = _a.sent();
488
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
489
- allowAnonymous: true,
490
- permissions: {
491
- create: [permissions_1.Permissions.IsAny],
492
- delete: [permissions_1.Permissions.IsAny],
493
- list: [permissions_1.Permissions.IsAny],
494
- read: [permissions_1.Permissions.IsAny],
495
- update: [permissions_1.Permissions.IsAny],
496
- },
497
- preUpdate: function () {
498
- var error = new Error("Some custom error");
499
- error.disableExternalErrorTracking = true;
500
- throw error;
501
- },
502
- }));
503
- server = (0, supertest_1.default)(app);
504
- return [4 /*yield*/, server
505
- .patch("/food/".concat(spinach._id))
506
- .send({
507
- name: "Broccoli",
508
- })
509
- .expect(400)];
510
- case 3:
511
- res = _a.sent();
512
- (0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook error");
513
- (0, bun_test_1.expect)(res.body.disableExternalErrorTracking).toBe(true);
514
- return [2 /*return*/];
515
- }
516
- });
517
- }); });
518
- (0, bun_test_1.it)("preDelete hook preserves disableExternalErrorTracking on non-APIError", function () { return __awaiter(void 0, void 0, void 0, function () {
519
- var notAdmin, spinach, res;
520
- return __generator(this, function (_a) {
521
- switch (_a.label) {
522
- case 0: return [4 /*yield*/, tests_1.UserModel.findOne({
523
- email: "notAdmin@example.com",
524
- })];
525
- case 1:
526
- notAdmin = _a.sent();
527
- return [4 /*yield*/, tests_1.FoodModel.create({
528
- calories: 1,
529
- created: new Date("2021-12-03T00:00:20.000Z"),
530
- hidden: false,
531
- name: "Spinach",
532
- ownerId: notAdmin._id,
533
- source: {
534
- name: "Brand",
535
- },
536
- })];
537
- case 2:
538
- spinach = _a.sent();
539
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
540
- allowAnonymous: true,
541
- permissions: {
542
- create: [permissions_1.Permissions.IsAny],
543
- delete: [permissions_1.Permissions.IsAny],
544
- list: [permissions_1.Permissions.IsAny],
545
- read: [permissions_1.Permissions.IsAny],
546
- update: [permissions_1.Permissions.IsAny],
547
- },
548
- preDelete: function () {
549
- var error = new Error("Some custom error");
550
- error.disableExternalErrorTracking = true;
551
- throw error;
552
- },
553
- }));
554
- server = (0, supertest_1.default)(app);
555
- return [4 /*yield*/, agent.delete("/food/".concat(spinach._id)).expect(403)];
556
- case 3:
557
- res = _a.sent();
558
- (0, bun_test_1.expect)(res.body.title).toContain("preDelete hook error");
559
- (0, bun_test_1.expect)(res.body.disableExternalErrorTracking).toBe(true);
560
- return [2 /*return*/];
561
- }
562
- });
563
- }); });
564
- });
565
- (0, bun_test_1.describe)("model array operations", function () {
566
- var admin;
567
- var spinach;
568
- var apple;
569
- var agent;
570
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
571
- var _a, _b;
572
- return __generator(this, function (_c) {
573
- switch (_c.label) {
574
- case 0:
575
- process.env.REFRESH_TOKEN_SECRET = "testsecret1234";
576
- return [4 /*yield*/, (0, tests_1.setupDb)()];
577
- case 1:
578
- _a = __read.apply(void 0, [_c.sent(), 1]), admin = _a[0];
112
+ _a = __read.apply(void 0, [_c.sent(), 2]), admin = _a[0], notAdmin = _a[1];
579
113
  return [4 /*yield*/, Promise.all([
580
114
  tests_1.FoodModel.create({
581
115
  calories: 1,
@@ -586,1683 +120,23 @@ var transformers_1 = require("./transformers");
586
120
  source: {
587
121
  name: "Brand",
588
122
  },
589
- }),
590
- tests_1.FoodModel.create({
591
- calories: 100,
592
- categories: [
593
- {
594
- name: "Fruit",
595
- show: true,
596
- },
597
- {
598
- name: "Popular",
599
- show: false,
600
- },
601
- ],
602
- created: new Date("2021-12-03T00:00:30.000Z"),
603
- hidden: false,
604
- name: "Apple",
605
- ownerId: admin._id,
606
- tags: ["healthy", "cheap"],
607
- }),
608
- ])];
609
- case 2:
610
- _b = __read.apply(void 0, [_c.sent(), 2]), spinach = _b[0], apple = _b[1];
611
- app = (0, tests_1.getBaseServer)();
612
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
613
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
614
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
615
- allowAnonymous: true,
616
- permissions: {
617
- create: [permissions_1.Permissions.IsAdmin],
618
- delete: [permissions_1.Permissions.IsAdmin],
619
- list: [permissions_1.Permissions.IsAdmin],
620
- read: [permissions_1.Permissions.IsAdmin],
621
- update: [permissions_1.Permissions.IsAdmin],
622
- },
623
- queryFields: ["hidden", "calories", "created", "source.name"],
624
- sort: { created: "descending" },
625
- }));
626
- server = (0, supertest_1.default)(app);
627
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
628
- case 3:
629
- agent = _c.sent();
630
- return [2 /*return*/];
631
- }
632
- });
633
- }); });
634
- (0, bun_test_1.it)("add array sub-schema item", function () { return __awaiter(void 0, void 0, void 0, function () {
635
- var res;
636
- return __generator(this, function (_a) {
637
- switch (_a.label) {
638
- case 0: return [4 /*yield*/, agent
639
- .post("/food/".concat(apple._id, "/categories"))
640
- .send({ name: "Good Seller", show: false })
641
- .expect(400)];
642
- case 1:
643
- res = _a.sent();
644
- (0, bun_test_1.expect)(res.body.title).toBe("Malformed body, array operations should have a single, top level key, got: name,show");
645
- return [4 /*yield*/, agent
646
- .post("/food/".concat(apple._id, "/categories"))
647
- .send({ categories: { name: "Good Seller", show: false } })
648
- .expect(200)];
649
- case 2:
650
- res = _a.sent();
651
- (0, bun_test_1.expect)(res.body.data.categories).toHaveLength(3);
652
- (0, bun_test_1.expect)(res.body.data.categories[2].name).toBe("Good Seller");
653
- return [4 /*yield*/, agent
654
- .post("/food/".concat(spinach._id, "/categories"))
655
- .send({ categories: { name: "Good Seller", show: false } })
656
- .expect(200)];
657
- case 3:
658
- res = _a.sent();
659
- (0, bun_test_1.expect)(res.body.data.categories).toHaveLength(1);
660
- return [2 /*return*/];
661
- }
662
- });
663
- }); });
664
- (0, bun_test_1.it)("update array sub-schema item", function () { return __awaiter(void 0, void 0, void 0, function () {
665
- var res;
666
- return __generator(this, function (_a) {
667
- switch (_a.label) {
668
- case 0: return [4 /*yield*/, agent
669
- .patch("/food/".concat(apple._id, "/categories/xyz"))
670
- .send({ categories: { name: "Good Seller", show: false } })
671
- .expect(404)];
672
- case 1:
673
- res = _a.sent();
674
- (0, bun_test_1.expect)(res.body.title).toBe("Could not find categories/xyz");
675
- return [4 /*yield*/, agent
676
- .patch("/food/".concat(apple._id, "/categories/").concat(apple.categories[1]._id))
677
- .send({ categories: { name: "Good Seller", show: false } })
678
- .expect(200)];
679
- case 2:
680
- res = _a.sent();
681
- (0, bun_test_1.expect)(res.body.data.categories).toHaveLength(2);
682
- (0, bun_test_1.expect)(res.body.data.categories[1].name).toBe("Good Seller");
683
- return [2 /*return*/];
684
- }
685
- });
686
- }); });
687
- (0, bun_test_1.it)("delete array sub-schema item", function () { return __awaiter(void 0, void 0, void 0, function () {
688
- var res;
689
- return __generator(this, function (_a) {
690
- switch (_a.label) {
691
- case 0: return [4 /*yield*/, agent.delete("/food/".concat(apple._id, "/categories/xyz")).expect(404)];
692
- case 1:
693
- res = _a.sent();
694
- (0, bun_test_1.expect)(res.body.title).toBe("Could not find categories/xyz");
695
- return [4 /*yield*/, agent
696
- .delete("/food/".concat(apple._id, "/categories/").concat(apple.categories[0]._id))
697
- .expect(200)];
698
- case 2:
699
- res = _a.sent();
700
- (0, bun_test_1.expect)(res.body.data.categories).toHaveLength(1);
701
- (0, bun_test_1.expect)(res.body.data.categories[0].name).toBe("Popular");
702
- return [2 /*return*/];
703
- }
704
- });
705
- }); });
706
- (0, bun_test_1.it)("add array item", function () { return __awaiter(void 0, void 0, void 0, function () {
707
- var res;
708
- return __generator(this, function (_a) {
709
- switch (_a.label) {
710
- case 0: return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "popular" }).expect(200)];
711
- case 1:
712
- res = _a.sent();
713
- (0, bun_test_1.expect)(res.body.data.tags).toHaveLength(3);
714
- (0, bun_test_1.expect)(res.body.data.tags).toEqual(["healthy", "cheap", "popular"]);
715
- return [4 /*yield*/, agent.post("/food/".concat(spinach._id, "/tags")).send({ tags: "popular" }).expect(200)];
716
- case 2:
717
- res = _a.sent();
718
- (0, bun_test_1.expect)(res.body.data.tags).toEqual(["popular"]);
719
- return [2 /*return*/];
720
- }
721
- });
722
- }); });
723
- (0, bun_test_1.it)("update array item", function () { return __awaiter(void 0, void 0, void 0, function () {
724
- var res;
725
- return __generator(this, function (_a) {
726
- switch (_a.label) {
727
- case 0: return [4 /*yield*/, agent
728
- .patch("/food/".concat(apple._id, "/tags/xyz"))
729
- .send({ tags: "unhealthy" })
730
- .expect(404)];
731
- case 1:
732
- res = _a.sent();
733
- (0, bun_test_1.expect)(res.body.title).toBe("Could not find tags/xyz");
734
- return [4 /*yield*/, agent
735
- .patch("/food/".concat(apple._id, "/tags/healthy"))
736
- .send({ tags: "unhealthy" })
737
- .expect(200)];
738
- case 2:
739
- res = _a.sent();
740
- (0, bun_test_1.expect)(res.body.data.tags).toEqual(["unhealthy", "cheap"]);
741
- return [2 /*return*/];
742
- }
743
- });
744
- }); });
745
- (0, bun_test_1.it)("delete array item", function () { return __awaiter(void 0, void 0, void 0, function () {
746
- var res;
747
- return __generator(this, function (_a) {
748
- switch (_a.label) {
749
- case 0: return [4 /*yield*/, agent.delete("/food/".concat(apple._id, "/tags/xyz")).expect(404)];
750
- case 1:
751
- res = _a.sent();
752
- (0, bun_test_1.expect)(res.body.title).toBe("Could not find tags/xyz");
753
- return [4 /*yield*/, agent.delete("/food/".concat(apple._id, "/tags/healthy")).expect(200)];
754
- case 2:
755
- res = _a.sent();
756
- (0, bun_test_1.expect)(res.body.data.tags).toEqual(["cheap"]);
757
- return [2 /*return*/];
758
- }
759
- });
760
- }); });
761
- (0, bun_test_1.it)("updates timestamps on array subdocuments", function () { return __awaiter(void 0, void 0, void 0, function () {
762
- var foodWithTimestamps, firstCategoryId, secondCategoryId, res, updatedCategory, unchangedCategory;
763
- var _a, _b, _c, _d, _e, _f;
764
- return __generator(this, function (_g) {
765
- switch (_g.label) {
766
- case 0: return [4 /*yield*/, tests_1.FoodModel.create({
767
- calories: 100,
768
- categories: [
769
- {
770
- name: "Category 1",
771
- show: true,
772
- updated: new Date("2024-01-01T00:00:00.000Z"),
773
- },
774
- {
775
- name: "Category 2",
776
- show: true,
777
- updated: new Date("2024-01-01T00:00:00.000Z"),
778
- },
779
- ],
780
- created: new Date(),
781
- name: "Food with Timestamps",
782
- ownerId: admin._id,
783
- })];
784
- case 1:
785
- foodWithTimestamps = _g.sent();
786
- firstCategoryId = (_c = (_b = (_a = foodWithTimestamps.categories) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b._id) === null || _c === void 0 ? void 0 : _c.toString();
787
- secondCategoryId = (_f = (_e = (_d = foodWithTimestamps.categories) === null || _d === void 0 ? void 0 : _d[1]) === null || _e === void 0 ? void 0 : _e._id) === null || _f === void 0 ? void 0 : _f.toString();
788
- if (!firstCategoryId || !secondCategoryId) {
789
- throw new Error("Failed to create food with categories");
790
- }
791
- // Wait a moment to ensure timestamp difference
792
- return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 100); })];
793
- case 2:
794
- // Wait a moment to ensure timestamp difference
795
- _g.sent();
796
- return [4 /*yield*/, agent
797
- .patch("/food/".concat(foodWithTimestamps._id, "/categories/").concat(firstCategoryId))
798
- .send({ categories: { name: "Updated Category" } })
799
- .expect(200)];
800
- case 3:
801
- res = _g.sent();
802
- updatedCategory = res.body.data.categories.find(function (c) { return c._id === firstCategoryId; });
803
- unchangedCategory = res.body.data.categories.find(function (c) { return c._id === secondCategoryId; });
804
- if (!updatedCategory || !unchangedCategory) {
805
- throw new Error("Failed to find categories in response");
806
- }
807
- (0, bun_test_1.expect)(updatedCategory.updated).not.toBe(updatedCategory.created);
808
- (0, bun_test_1.expect)(unchangedCategory.updated).toBe(unchangedCategory.created);
809
- (0, bun_test_1.expect)(updatedCategory.name).toBe("Updated Category");
810
- // Unchanged.
811
- (0, bun_test_1.expect)(updatedCategory.show).toBe(true);
812
- (0, bun_test_1.expect)(unchangedCategory.show).toBe(true);
813
- return [2 /*return*/];
814
- }
815
- });
816
- }); });
817
- (0, bun_test_1.it)("array operations call postUpdate with different copy of document", function () { return __awaiter(void 0, void 0, void 0, function () {
818
- var postUpdateDoc, postUpdatePrevDoc, postUpdateCalled, categoryId, updatedCategory, prevCategory, remainingCategories, prevCategories;
819
- return __generator(this, function (_a) {
820
- switch (_a.label) {
821
- case 0:
822
- postUpdateCalled = false;
823
- app = (0, tests_1.getBaseServer)();
824
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
825
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
826
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
827
- allowAnonymous: true,
828
- permissions: {
829
- create: [permissions_1.Permissions.IsAdmin],
830
- delete: [permissions_1.Permissions.IsAdmin],
831
- list: [permissions_1.Permissions.IsAdmin],
832
- read: [permissions_1.Permissions.IsAdmin],
833
- update: [permissions_1.Permissions.IsAdmin],
834
- },
835
- postUpdate: function (doc, _cleanedBody, _request, prevValue) { return __awaiter(void 0, void 0, void 0, function () {
836
- return __generator(this, function (_a) {
837
- postUpdateDoc = doc;
838
- postUpdatePrevDoc = prevValue;
839
- postUpdateCalled = true;
840
- return [2 /*return*/];
841
- });
842
- }); },
843
- }));
844
- server = (0, supertest_1.default)(app);
845
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
846
- case 1:
847
- agent = _a.sent();
848
- // Test POST operation (add to array)
849
- return [4 /*yield*/, agent
850
- .post("/food/".concat(apple._id, "/categories"))
851
- .send({ categories: { name: "New Category", show: true } })
852
- .expect(200)];
853
- case 2:
854
- // Test POST operation (add to array)
855
- _a.sent();
856
- (0, bun_test_1.expect)(postUpdateCalled).toBe(true);
857
- (0, bun_test_1.expect)(postUpdateDoc).toBeDefined();
858
- (0, bun_test_1.expect)(postUpdatePrevDoc).toBeDefined();
859
- // Verify they are different object references
860
- (0, bun_test_1.expect)(postUpdateDoc).not.toBe(postUpdatePrevDoc);
861
- // Verify the content is different (new category added)
862
- (0, bun_test_1.expect)(postUpdateDoc.categories).toHaveLength(3);
863
- (0, bun_test_1.expect)(postUpdatePrevDoc.categories).toHaveLength(2);
864
- // Reset for next test
865
- postUpdateCalled = false;
866
- postUpdateDoc = undefined;
867
- postUpdatePrevDoc = undefined;
868
- categoryId = apple.categories[0]._id;
869
- if (!categoryId) {
870
- throw new Error("Category ID is undefined");
871
- }
872
- return [4 /*yield*/, agent
873
- .patch("/food/".concat(apple._id, "/categories/").concat(categoryId))
874
- .send({ categories: { name: "Updated Category", show: false } })
875
- .expect(200)];
876
- case 3:
877
- _a.sent();
878
- (0, bun_test_1.expect)(postUpdateCalled).toBe(true);
879
- (0, bun_test_1.expect)(postUpdateDoc).toBeDefined();
880
- (0, bun_test_1.expect)(postUpdatePrevDoc).toBeDefined();
881
- // Verify they are different object references
882
- (0, bun_test_1.expect)(postUpdateDoc).not.toBe(postUpdatePrevDoc);
883
- updatedCategory = postUpdateDoc.categories.find(function (c) { return c._id.toString() === categoryId.toString(); });
884
- prevCategory = postUpdatePrevDoc.categories.find(function (c) { return c._id.toString() === categoryId.toString(); });
885
- (0, bun_test_1.expect)(updatedCategory.name).toBe("Updated Category");
886
- (0, bun_test_1.expect)(prevCategory.name).toBe("Fruit");
887
- // Reset for next test
888
- postUpdateCalled = false;
889
- postUpdateDoc = undefined;
890
- postUpdatePrevDoc = undefined;
891
- // Test DELETE operation (remove from array)
892
- return [4 /*yield*/, agent.delete("/food/".concat(apple._id, "/categories/").concat(categoryId)).expect(200)];
893
- case 4:
894
- // Test DELETE operation (remove from array)
895
- _a.sent();
896
- (0, bun_test_1.expect)(postUpdateCalled).toBe(true);
897
- (0, bun_test_1.expect)(postUpdateDoc).toBeDefined();
898
- (0, bun_test_1.expect)(postUpdatePrevDoc).toBeDefined();
899
- // Verify they are different object references
900
- (0, bun_test_1.expect)(postUpdateDoc).not.toBe(postUpdatePrevDoc);
901
- remainingCategories = postUpdateDoc.categories.filter(function (c) { return c._id.toString() === categoryId.toString(); });
902
- prevCategories = postUpdatePrevDoc.categories.filter(function (c) { return c._id.toString() === categoryId.toString(); });
903
- (0, bun_test_1.expect)(remainingCategories).toHaveLength(0);
904
- (0, bun_test_1.expect)(prevCategories).toHaveLength(1);
905
- return [2 /*return*/];
906
- }
907
- });
908
- }); });
909
- (0, bun_test_1.it)("array operations with string arrays call postUpdate with different copy", function () { return __awaiter(void 0, void 0, void 0, function () {
910
- var postUpdateDoc, postUpdatePrevDoc, postUpdateCalled;
911
- return __generator(this, function (_a) {
912
- switch (_a.label) {
913
- case 0:
914
- postUpdateCalled = false;
915
- app = (0, tests_1.getBaseServer)();
916
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
917
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
918
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
919
- allowAnonymous: true,
920
- permissions: {
921
- create: [permissions_1.Permissions.IsAdmin],
922
- delete: [permissions_1.Permissions.IsAdmin],
923
- list: [permissions_1.Permissions.IsAdmin],
924
- read: [permissions_1.Permissions.IsAdmin],
925
- update: [permissions_1.Permissions.IsAdmin],
926
- },
927
- postUpdate: function (doc, _cleanedBody, _request, prevValue) { return __awaiter(void 0, void 0, void 0, function () {
928
- return __generator(this, function (_a) {
929
- postUpdateDoc = doc;
930
- postUpdatePrevDoc = prevValue;
931
- postUpdateCalled = true;
932
- return [2 /*return*/];
933
- });
934
- }); },
935
- }));
936
- server = (0, supertest_1.default)(app);
937
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
938
- case 1:
939
- agent = _a.sent();
940
- // Test POST operation with string array (add tag)
941
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(200)];
942
- case 2:
943
- // Test POST operation with string array (add tag)
944
- _a.sent();
945
- (0, bun_test_1.expect)(postUpdateCalled).toBe(true);
946
- (0, bun_test_1.expect)(postUpdateDoc).toBeDefined();
947
- (0, bun_test_1.expect)(postUpdatePrevDoc).toBeDefined();
948
- // Verify they are different object references
949
- (0, bun_test_1.expect)(postUpdateDoc).not.toBe(postUpdatePrevDoc);
950
- // Verify the content is different (new tag added)
951
- (0, bun_test_1.expect)(postUpdateDoc.tags).toHaveLength(3);
952
- (0, bun_test_1.expect)(postUpdatePrevDoc.tags).toHaveLength(2);
953
- (0, bun_test_1.expect)(postUpdateDoc.tags).toContain("organic");
954
- (0, bun_test_1.expect)(postUpdatePrevDoc.tags).not.toContain("organic");
955
- // Reset for next test
956
- postUpdateCalled = false;
957
- postUpdateDoc = undefined;
958
- postUpdatePrevDoc = undefined;
959
- // Test PATCH operation with string array (update tag)
960
- return [4 /*yield*/, agent
961
- .patch("/food/".concat(apple._id, "/tags/healthy"))
962
- .send({ tags: "super-healthy" })
963
- .expect(200)];
964
- case 3:
965
- // Test PATCH operation with string array (update tag)
966
- _a.sent();
967
- (0, bun_test_1.expect)(postUpdateCalled).toBe(true);
968
- (0, bun_test_1.expect)(postUpdateDoc).not.toBe(postUpdatePrevDoc);
969
- // Verify the content is different (tag updated)
970
- (0, bun_test_1.expect)(postUpdateDoc.tags).toContain("super-healthy");
971
- (0, bun_test_1.expect)(postUpdatePrevDoc.tags).toContain("healthy");
972
- (0, bun_test_1.expect)(postUpdateDoc.tags).not.toContain("healthy");
973
- (0, bun_test_1.expect)(postUpdatePrevDoc.tags).not.toContain("super-healthy");
974
- return [2 /*return*/];
975
- }
976
- });
977
- }); });
978
- });
979
- (0, bun_test_1.describe)("standard methods", function () {
980
- var notAdmin;
981
- var admin;
982
- var adminOther;
983
- var agent;
984
- var spinach;
985
- var apple;
986
- var carrots;
987
- var pizza;
988
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
989
- var results;
990
- var _a, _b;
991
- return __generator(this, function (_c) {
992
- switch (_c.label) {
993
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
994
- case 1:
995
- _a = __read.apply(void 0, [_c.sent(), 3]), admin = _a[0], notAdmin = _a[1], adminOther = _a[2];
996
- return [4 /*yield*/, Promise.all([
997
- tests_1.FoodModel.create({
998
- calories: 1,
999
- created: new Date("2021-12-03T00:00:20.000Z"),
1000
- eatenBy: [admin._id],
1001
- hidden: false,
1002
- lastEatenWith: {
1003
- dressing: new Date("2021-12-03T19:00:30.000Z"),
1004
- },
1005
- name: "Spinach",
1006
- ownerId: notAdmin._id,
1007
- source: {
1008
- dateAdded: "2023-12-13T12:30:00.000Z",
1009
- href: "https://www.google.com",
1010
- name: "Brand",
1011
- },
1012
- }),
1013
- tests_1.FoodModel.create({
1014
- calories: 100,
1015
- created: new Date("2021-12-03T00:00:30.000Z"),
1016
- hidden: true,
1017
- name: "Apple",
1018
- ownerId: admin._id,
1019
- tags: ["healthy"],
1020
- }),
1021
- tests_1.FoodModel.create({
1022
- calories: 100,
1023
- created: new Date("2021-12-03T00:00:00.000Z"),
1024
- eatenBy: [admin._id, notAdmin._id],
1025
- hidden: false,
1026
- name: "Carrots",
1027
- ownerId: admin._id,
1028
- source: {
1029
- name: "USDA",
1030
- },
1031
- tags: ["healthy", "cheap"],
1032
- }),
1033
- tests_1.FoodModel.create({
1034
- calories: 400,
1035
- created: new Date("2021-12-03T00:00:10.000Z"),
1036
- eatenBy: [adminOther._id],
1037
- hidden: false,
1038
- name: "Pizza",
1039
- ownerId: admin._id,
1040
- tags: ["cheap"],
1041
- }),
1042
- ])];
1043
- case 2:
1044
- results = (_c.sent());
1045
- _b = __read(results, 4), spinach = _b[0], apple = _b[1], carrots = _b[2], pizza = _b[3];
1046
- app = (0, tests_1.getBaseServer)();
1047
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
1048
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
1049
- app.use(expressServer_1.logRequests);
1050
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1051
- allowAnonymous: true,
1052
- defaultLimit: 2,
1053
- defaultQueryParams: { hidden: false },
1054
- maxLimit: 3,
1055
- permissions: {
1056
- create: [permissions_1.Permissions.IsAuthenticated],
1057
- delete: [permissions_1.Permissions.IsAdmin],
1058
- list: [permissions_1.Permissions.IsAny],
1059
- read: [permissions_1.Permissions.IsAny],
1060
- update: [permissions_1.Permissions.IsOwner],
1061
- },
1062
- populatePaths: [{ path: "ownerId" }],
1063
- queryFields: ["hidden", "name", "calories", "created", "source.name", "tags", "eatenBy"],
1064
- sort: { created: "descending" },
1065
- }));
1066
- server = (0, supertest_1.default)(app);
1067
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
1068
- case 3:
1069
- agent = _c.sent();
1070
- return [2 /*return*/];
1071
- }
1072
- });
1073
- }); });
1074
- (0, bun_test_1.it)("read default", function () { return __awaiter(void 0, void 0, void 0, function () {
1075
- var res;
1076
- return __generator(this, function (_a) {
1077
- switch (_a.label) {
1078
- case 0: return [4 /*yield*/, agent.get("/food/".concat(spinach._id)).expect(200)];
1079
- case 1:
1080
- res = _a.sent();
1081
- (0, bun_test_1.expect)(res.body.data._id).toBe(spinach._id.toString());
1082
- // Ensure populate works
1083
- (0, bun_test_1.expect)(res.body.data.ownerId._id).toBe(notAdmin.id);
1084
- // Ensure maps are properly transformed
1085
- (0, bun_test_1.expect)(res.body.data.lastEatenWith).toEqual({
1086
- dressing: "2021-12-03T19:00:30.000Z",
1087
- });
1088
- return [2 /*return*/];
1089
- }
1090
- });
1091
- }); });
1092
- (0, bun_test_1.it)("list default", function () { return __awaiter(void 0, void 0, void 0, function () {
1093
- var res;
1094
- return __generator(this, function (_a) {
1095
- switch (_a.label) {
1096
- case 0: return [4 /*yield*/, agent.get("/food").expect(200)];
1097
- case 1:
1098
- res = _a.sent();
1099
- (0, bun_test_1.expect)(res.body.data).toHaveLength(2);
1100
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(spinach.id);
1101
- (0, bun_test_1.expect)(res.body.data[0].ownerId._id).toBe(notAdmin.id);
1102
- (0, bun_test_1.expect)(res.body.data[1].id).toBe(pizza.id);
1103
- (0, bun_test_1.expect)(res.body.data[1].ownerId._id).toBe(admin.id);
1104
- // Check that mongoose Map is handled correctly.
1105
- (0, bun_test_1.expect)(res.body.data[0].lastEatenWith).toEqual({
1106
- dressing: "2021-12-03T19:00:30.000Z",
1107
- });
1108
- (0, bun_test_1.expect)(res.body.data[1].lastEatenWith).toEqual(undefined);
1109
- (0, bun_test_1.expect)(res.body.more).toBe(true);
1110
- (0, bun_test_1.expect)(res.body.total).toBe(3);
1111
- return [2 /*return*/];
1112
- }
1113
- });
1114
- }); });
1115
- (0, bun_test_1.it)("list limit", function () { return __awaiter(void 0, void 0, void 0, function () {
1116
- var res;
1117
- return __generator(this, function (_a) {
1118
- switch (_a.label) {
1119
- case 0: return [4 /*yield*/, agent.get("/food?limit=1").expect(200)];
1120
- case 1:
1121
- res = _a.sent();
1122
- (0, bun_test_1.expect)(res.body.data).toHaveLength(1);
1123
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(spinach.id);
1124
- (0, bun_test_1.expect)(res.body.data[0].ownerId._id).toBe(notAdmin.id);
1125
- (0, bun_test_1.expect)(res.body.more).toBe(true);
1126
- (0, bun_test_1.expect)(res.body.total).toBe(3);
1127
- return [2 /*return*/];
1128
- }
1129
- });
1130
- }); });
1131
- (0, bun_test_1.it)("list limit over", function () { return __awaiter(void 0, void 0, void 0, function () {
1132
- var res;
1133
- return __generator(this, function (_a) {
1134
- switch (_a.label) {
1135
- case 0:
1136
- // This shouldn't be seen, it's the end of the list.
1137
- return [4 /*yield*/, tests_1.FoodModel.create({
1138
- calories: 400,
1139
- created: new Date("2021-12-02T00:00:10.000Z"),
1140
- hidden: false,
1141
- name: "Pizza",
1142
- ownerId: admin._id,
1143
- })];
1144
- case 1:
1145
- // This shouldn't be seen, it's the end of the list.
1146
- _a.sent();
1147
- return [4 /*yield*/, agent.get("/food?limit=4").expect(200)];
1148
- case 2:
1149
- res = _a.sent();
1150
- (0, bun_test_1.expect)(res.body.data).toHaveLength(3);
1151
- (0, bun_test_1.expect)(res.body.more).toBe(true);
1152
- (0, bun_test_1.expect)(res.body.total).toBe(4);
1153
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(spinach.id);
1154
- (0, bun_test_1.expect)(res.body.data[1].id).toBe(pizza.id);
1155
- (0, bun_test_1.expect)(res.body.data[2].id).toBe(carrots.id);
1156
- (0, bun_test_1.expect)(Sentry.captureMessage).toHaveBeenCalledWith('More than 3 results returned for foods without pagination, data may be silently truncated. req.query: {"limit":"4"}');
1157
- return [2 /*return*/];
1158
- }
1159
- });
1160
- }); });
1161
- (0, bun_test_1.it)("list page", function () { return __awaiter(void 0, void 0, void 0, function () {
1162
- var res;
1163
- return __generator(this, function (_a) {
1164
- switch (_a.label) {
1165
- case 0: return [4 /*yield*/, agent.get("/food?limit=1&page=2").expect(200)];
1166
- case 1:
1167
- res = _a.sent();
1168
- (0, bun_test_1.expect)(res.body.data).toHaveLength(1);
1169
- (0, bun_test_1.expect)(res.body.more).toBe(true);
1170
- (0, bun_test_1.expect)(res.body.total).toBe(3);
1171
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(pizza.id);
1172
- return [2 /*return*/];
1173
- }
1174
- });
1175
- }); });
1176
- (0, bun_test_1.it)("list page 0 ", function () { return __awaiter(void 0, void 0, void 0, function () {
1177
- var res;
1178
- return __generator(this, function (_a) {
1179
- switch (_a.label) {
1180
- case 0: return [4 /*yield*/, agent.get("/food?limit=1&page=0").expect(400)];
1181
- case 1:
1182
- res = _a.sent();
1183
- (0, bun_test_1.expect)(res.body.title).toBe("Invalid page: 0");
1184
- return [2 /*return*/];
1185
- }
1186
- });
1187
- }); });
1188
- (0, bun_test_1.it)("list page with garbage ", function () { return __awaiter(void 0, void 0, void 0, function () {
1189
- var res;
1190
- return __generator(this, function (_a) {
1191
- switch (_a.label) {
1192
- case 0: return [4 /*yield*/, agent.get("/food?limit=1&page=abc").expect(400)];
1193
- case 1:
1194
- res = _a.sent();
1195
- (0, bun_test_1.expect)(res.body.title).toBe("Invalid page: abc");
1196
- return [2 /*return*/];
1197
- }
1198
- });
1199
- }); });
1200
- (0, bun_test_1.it)("list page over", function () { return __awaiter(void 0, void 0, void 0, function () {
1201
- var res;
1202
- return __generator(this, function (_a) {
1203
- switch (_a.label) {
1204
- case 0: return [4 /*yield*/, agent.get("/food?limit=1&page=5").expect(200)];
1205
- case 1:
1206
- res = _a.sent();
1207
- (0, bun_test_1.expect)(res.body.data).toHaveLength(0);
1208
- (0, bun_test_1.expect)(res.body.more).toBe(false);
1209
- (0, bun_test_1.expect)(res.body.total).toBe(3);
1210
- return [2 /*return*/];
1211
- }
1212
- });
1213
- }); });
1214
- (0, bun_test_1.it)("list query params", function () { return __awaiter(void 0, void 0, void 0, function () {
1215
- var res;
1216
- return __generator(this, function (_a) {
1217
- switch (_a.label) {
1218
- case 0: return [4 /*yield*/, agent.get("/food?hidden=true").expect(200)];
1219
- case 1:
1220
- res = _a.sent();
1221
- (0, bun_test_1.expect)(res.body.data).toHaveLength(1);
1222
- (0, bun_test_1.expect)(res.body.more).toBe(false);
1223
- (0, bun_test_1.expect)(res.body.total).toBe(1);
1224
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(apple.id);
1225
- return [2 /*return*/];
1226
- }
1227
- });
1228
- }); });
1229
- (0, bun_test_1.it)("list query params not in list", function () { return __awaiter(void 0, void 0, void 0, function () {
1230
- var res;
1231
- return __generator(this, function (_a) {
1232
- switch (_a.label) {
1233
- case 0: return [4 /*yield*/, agent.get("/food?ownerId=".concat(admin._id)).expect(400)];
1234
- case 1:
1235
- res = _a.sent();
1236
- (0, bun_test_1.expect)(res.body.title).toBe("ownerId is not allowed as a query param.");
1237
- return [2 /*return*/];
1238
- }
1239
- });
1240
- }); });
1241
- (0, bun_test_1.it)("list query by nested param", function () { return __awaiter(void 0, void 0, void 0, function () {
1242
- var res;
1243
- return __generator(this, function (_a) {
1244
- switch (_a.label) {
1245
- case 0: return [4 /*yield*/, agent.get("/food?source.name=USDA").expect(200)];
1246
- case 1:
1247
- res = _a.sent();
1248
- (0, bun_test_1.expect)(res.body.data).toHaveLength(1);
1249
- (0, bun_test_1.expect)(res.body.total).toBe(1);
1250
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(carrots.id);
1251
- return [2 /*return*/];
1252
- }
1253
- });
1254
- }); });
1255
- (0, bun_test_1.it)("query by date", function () { return __awaiter(void 0, void 0, void 0, function () {
1256
- var authRes, token, res, createdDates;
1257
- return __generator(this, function (_a) {
1258
- switch (_a.label) {
1259
- case 0: return [4 /*yield*/, server
1260
- .post("/auth/login")
1261
- .send({ email: "admin@example.com", password: "securePassword" })
1262
- .expect(200)];
1263
- case 1:
1264
- authRes = _a.sent();
1265
- token = authRes.body.data.token;
1266
- return [4 /*yield*/, server
1267
- .get("/food?limit=3&".concat(qs_1.default.stringify({
1268
- created: {
1269
- $gte: "2021-12-03T00:00:00.000Z",
1270
- $lte: "2021-12-03T00:00:20.000Z",
1271
- },
1272
- })))
1273
- .set("authorization", "Bearer ".concat(token))
1274
- .expect(200)];
1275
- case 2:
1276
- res = _a.sent();
1277
- (0, bun_test_1.expect)(res.body.data.map(function (d) { return d.created; })).toEqual(bun_test_1.expect.arrayContaining([
1278
- "2021-12-03T00:00:20.000Z",
1279
- "2021-12-03T00:00:10.000Z",
1280
- "2021-12-03T00:00:00.000Z",
1281
- ]));
1282
- (0, bun_test_1.expect)(res.body.data.map(function (d) { return d.created; })).toHaveLength(3);
1283
- return [4 /*yield*/, server
1284
- .get("/food?limit=3&".concat(qs_1.default.stringify({
1285
- created: {
1286
- $gte: "2021-12-03T00:00:00.000Z",
1287
- $lt: "2021-12-03T00:00:20.000Z",
1288
- },
1289
- })))
1290
- .set("authorization", "Bearer ".concat(token))
1291
- .expect(200)];
1292
- case 3:
1293
- // Inclusive one side
1294
- res = _a.sent();
1295
- (0, bun_test_1.expect)(res.body.data.map(function (d) { return d.created; })).toEqual(bun_test_1.expect.arrayContaining(["2021-12-03T00:00:10.000Z", "2021-12-03T00:00:00.000Z"]));
1296
- (0, bun_test_1.expect)(res.body.data.map(function (d) { return d.created; })).toHaveLength(2);
1297
- return [4 /*yield*/, server
1298
- .get("/food?limit=3&".concat(qs_1.default.stringify({
1299
- created: {
1300
- $gt: "2021-12-03T00:00:00.000Z",
1301
- $lt: "2021-12-03T00:00:20.000Z",
1302
- },
1303
- })))
1304
- .set("authorization", "Bearer ".concat(token))
1305
- .expect(200)];
1306
- case 4:
1307
- // Inclusive both sides
1308
- res = _a.sent();
1309
- createdDates = res.body.data.map(function (d) { return d.created; });
1310
- (0, bun_test_1.expect)(createdDates).toEqual(bun_test_1.expect.arrayContaining(["2021-12-03T00:00:10.000Z"]));
1311
- (0, bun_test_1.expect)(createdDates).toHaveLength(1);
1312
- return [2 /*return*/];
1313
- }
1314
- });
1315
- }); });
1316
- (0, bun_test_1.it)("query with a space", function () { return __awaiter(void 0, void 0, void 0, function () {
1317
- var greenBeans, res;
1318
- return __generator(this, function (_a) {
1319
- switch (_a.label) {
1320
- case 0: return [4 /*yield*/, tests_1.FoodModel.create({
1321
- calories: 102,
1322
- created: Date.now() - 10,
1323
- name: "Green Beans",
1324
- ownerId: admin === null || admin === void 0 ? void 0 : admin._id,
1325
- })];
1326
- case 1:
1327
- greenBeans = _a.sent();
1328
- return [4 /*yield*/, agent.get("/food?".concat(qs_1.default.stringify({ name: "Green Beans" }))).expect(200)];
1329
- case 2:
1330
- res = _a.sent();
1331
- (0, bun_test_1.expect)(res.body.data).toHaveLength(1);
1332
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(greenBeans === null || greenBeans === void 0 ? void 0 : greenBeans.id);
1333
- (0, bun_test_1.expect)(res.body.data[0].name).toBe("Green Beans");
1334
- return [2 /*return*/];
1335
- }
1336
- });
1337
- }); });
1338
- (0, bun_test_1.it)("query with a regex", function () { return __awaiter(void 0, void 0, void 0, function () {
1339
- var greenBeans, res;
1340
- return __generator(this, function (_a) {
1341
- switch (_a.label) {
1342
- case 0: return [4 /*yield*/, tests_1.FoodModel.create({
1343
- calories: 102,
1344
- created: Date.now() - 10,
1345
- name: "Green Beans",
1346
- ownerId: admin === null || admin === void 0 ? void 0 : admin._id,
1347
- })];
1348
- case 1:
1349
- greenBeans = _a.sent();
1350
- return [4 /*yield*/, agent.get("/food?".concat(qs_1.default.stringify({ name: { $regex: "Green" } }))).expect(200)];
1351
- case 2:
1352
- res = _a.sent();
1353
- (0, bun_test_1.expect)(res.body.data).toHaveLength(1);
1354
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(greenBeans === null || greenBeans === void 0 ? void 0 : greenBeans.id);
1355
- (0, bun_test_1.expect)(res.body.data[0].name).toBe("Green Beans");
1356
- return [4 /*yield*/, agent.get("/food?".concat(qs_1.default.stringify({ name: { $regex: "green" } }))).expect(200)];
1357
- case 3:
1358
- // Fails with different casing and sensitive
1359
- res = _a.sent();
1360
- (0, bun_test_1.expect)(res.body.data).toHaveLength(0);
1361
- return [4 /*yield*/, agent
1362
- .get("/food?".concat(qs_1.default.stringify({ name: { $options: "i", $regex: "green" } })))
1363
- .expect(200)];
1364
- case 4:
1365
- // Case insensitive does match different casing
1366
- res = _a.sent();
1367
- (0, bun_test_1.expect)(res.body.data).toHaveLength(1);
1368
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(greenBeans === null || greenBeans === void 0 ? void 0 : greenBeans.id);
1369
- return [2 /*return*/];
1370
- }
1371
- });
1372
- }); });
1373
- (0, bun_test_1.it)("query with an $in operator", function () { return __awaiter(void 0, void 0, void 0, function () {
1374
- var res, names1, names2;
1375
- return __generator(this, function (_a) {
1376
- switch (_a.label) {
1377
- case 0: return [4 /*yield*/, server
1378
- .get("/food?".concat(qs_1.default.stringify({
1379
- name: {
1380
- $in: ["Apple", "Spinach"],
1381
- },
1382
- })))
1383
- .expect(200)];
1384
- case 1:
1385
- res = _a.sent();
1386
- names1 = res.body.data.map(function (d) { return d.name; });
1387
- (0, bun_test_1.expect)(names1).toEqual(bun_test_1.expect.arrayContaining(["Spinach"]));
1388
- (0, bun_test_1.expect)(names1).toHaveLength(1);
1389
- return [4 /*yield*/, server
1390
- .get("/food?".concat(qs_1.default.stringify({
1391
- name: {
1392
- $in: ["Carrots", "Spinach"],
1393
- },
1394
- })))
1395
- .expect(200)];
1396
- case 2:
1397
- // Query without hidden food.
1398
- res = _a.sent();
1399
- names2 = res.body.data.map(function (d) { return d.name; });
1400
- (0, bun_test_1.expect)(names2).toEqual(bun_test_1.expect.arrayContaining(["Spinach", "Carrots"]));
1401
- (0, bun_test_1.expect)(names2).toHaveLength(2);
1402
- return [2 /*return*/];
1403
- }
1404
- });
1405
- }); });
1406
- (0, bun_test_1.it)("query with an $in for _ids in nested object", function () { return __awaiter(void 0, void 0, void 0, function () {
1407
- var res, names3;
1408
- return __generator(this, function (_a) {
1409
- switch (_a.label) {
1410
- case 0: return [4 /*yield*/, server
1411
- .get("/food?".concat(qs_1.default.stringify({
1412
- eatenBy: {
1413
- $in: [notAdmin._id.toString(), adminOther._id.toString()],
1414
- },
1415
- })))
1416
- .expect(200)];
1417
- case 1:
1418
- res = _a.sent();
1419
- (0, bun_test_1.expect)(res.body.more).toBe(false);
1420
- (0, bun_test_1.expect)(res.body.total).toBe(2);
1421
- (0, bun_test_1.expect)(res.body.data).toHaveLength(2);
1422
- names3 = res.body.data.map(function (d) { return d.name; });
1423
- (0, bun_test_1.expect)(names3).toEqual(bun_test_1.expect.arrayContaining(["Carrots", "Pizza"]));
1424
- (0, bun_test_1.expect)(names3).toHaveLength(2);
1425
- return [2 /*return*/];
1426
- }
1427
- });
1428
- }); });
1429
- (0, bun_test_1.it)("query $and operator on same field", function () { return __awaiter(void 0, void 0, void 0, function () {
1430
- var res;
1431
- return __generator(this, function (_a) {
1432
- switch (_a.label) {
1433
- case 0: return [4 /*yield*/, agent
1434
- .get("/food?".concat(qs_1.default.stringify({ $and: [{ tags: "healthy" }, { tags: "cheap" }] })))
1435
- .expect(200)];
1436
- case 1:
1437
- res = _a.sent();
1438
- (0, bun_test_1.expect)(res.body.data).toHaveLength(1);
1439
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(carrots === null || carrots === void 0 ? void 0 : carrots._id.toString());
1440
- return [2 /*return*/];
1441
- }
1442
- });
1443
- }); });
1444
- (0, bun_test_1.it)("query $and operator on same field, nested objects", function () { return __awaiter(void 0, void 0, void 0, function () {
1445
- var res;
1446
- return __generator(this, function (_a) {
1447
- switch (_a.label) {
1448
- case 0: return [4 /*yield*/, agent
1449
- .get("/food?".concat(qs_1.default.stringify({
1450
- $and: [{ eatenBy: admin.id }, { eatenBy: notAdmin.id }],
1451
- })))
1452
- .expect(200)];
1453
- case 1:
1454
- res = _a.sent();
1455
- (0, bun_test_1.expect)(res.body.data).toHaveLength(1);
1456
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(carrots === null || carrots === void 0 ? void 0 : carrots._id.toString());
1457
- return [2 /*return*/];
1458
- }
1459
- });
1460
- }); });
1461
- (0, bun_test_1.it)("query $or operator on same field", function () { return __awaiter(void 0, void 0, void 0, function () {
1462
- var res, ids1;
1463
- return __generator(this, function (_a) {
1464
- switch (_a.label) {
1465
- case 0: return [4 /*yield*/, agent
1466
- .get("/food?".concat(qs_1.default.stringify({ $or: [{ name: "Carrots" }, { name: "Pizza" }] })))
1467
- .expect(200)];
1468
- case 1:
1469
- res = _a.sent();
1470
- (0, bun_test_1.expect)(res.body.data).toHaveLength(2);
1471
- ids1 = res.body.data.map(function (d) { return d.id; });
1472
- (0, bun_test_1.expect)(ids1).toEqual(bun_test_1.expect.arrayContaining([carrots === null || carrots === void 0 ? void 0 : carrots._id.toString(), pizza === null || pizza === void 0 ? void 0 : pizza._id.toString()]));
1473
- (0, bun_test_1.expect)(ids1).toHaveLength(2);
1474
- return [2 /*return*/];
1475
- }
1476
- });
1477
- }); });
1478
- (0, bun_test_1.it)("query $and operator on same field, nested objects", function () { return __awaiter(void 0, void 0, void 0, function () {
1479
- var res, ids2;
1480
- return __generator(this, function (_a) {
1481
- switch (_a.label) {
1482
- case 0: return [4 /*yield*/, agent
1483
- .get("/food?".concat(qs_1.default.stringify({
1484
- $or: [{ eatenBy: admin.id }, { eatenBy: notAdmin.id }],
1485
- limit: 3,
1486
- })))
1487
- .expect(200)];
1488
- case 1:
1489
- res = _a.sent();
1490
- (0, bun_test_1.expect)(res.body.data).toHaveLength(2);
1491
- ids2 = res.body.data.map(function (d) { return d.id; });
1492
- (0, bun_test_1.expect)(ids2).toEqual(bun_test_1.expect.arrayContaining([carrots === null || carrots === void 0 ? void 0 : carrots._id.toString(), spinach === null || spinach === void 0 ? void 0 : spinach._id.toString()]));
1493
- (0, bun_test_1.expect)(ids2).toHaveLength(2);
1494
- return [2 /*return*/];
1495
- }
1496
- });
1497
- }); });
1498
- (0, bun_test_1.it)("query $and and $or are rejected if field is not in queryFields", function () { return __awaiter(void 0, void 0, void 0, function () {
1499
- var res;
1500
- return __generator(this, function (_a) {
1501
- switch (_a.label) {
1502
- case 0: return [4 /*yield*/, agent
1503
- .get("/food?".concat(qs_1.default.stringify({ $and: [{ ownerId: "healthy" }, { tags: "cheap" }] })))
1504
- .expect(400)];
1505
- case 1:
1506
- res = _a.sent();
1507
- (0, bun_test_1.expect)(res.body.title).toBe("ownerId is not allowed as a query param.");
1508
- return [4 /*yield*/, agent
1509
- .get("/food?".concat(qs_1.default.stringify({ $and: [{ tags: "cheap" }, { ownerId: "healthy" }] })))
1510
- .expect(400)];
1511
- case 2:
1512
- // Check in the other order
1513
- res = _a.sent();
1514
- (0, bun_test_1.expect)(res.body.title).toBe("ownerId is not allowed as a query param.");
1515
- return [4 /*yield*/, agent
1516
- .get("/food?".concat(qs_1.default.stringify({ $or: [{ tags: "cheap" }, { ownerId: "healthy" }] })))
1517
- .expect(400)];
1518
- case 3:
1519
- res = _a.sent();
1520
- (0, bun_test_1.expect)(res.body.title).toBe("ownerId is not allowed as a query param.");
1521
- return [2 /*return*/];
1522
- }
1523
- });
1524
- }); });
1525
- (0, bun_test_1.it)("query with a number", function () { return __awaiter(void 0, void 0, void 0, function () {
1526
- var res;
1527
- return __generator(this, function (_a) {
1528
- switch (_a.label) {
1529
- case 0: return [4 /*yield*/, agent.get("/food?calories=100").expect(200)];
1530
- case 1:
1531
- res = _a.sent();
1532
- (0, bun_test_1.expect)(res.body.data).toHaveLength(1);
1533
- (0, bun_test_1.expect)(res.body.data[0].id).toBe(carrots === null || carrots === void 0 ? void 0 : carrots._id.toString());
1534
- return [2 /*return*/];
1535
- }
1536
- });
1537
- }); });
1538
- (0, bun_test_1.it)("update", function () { return __awaiter(void 0, void 0, void 0, function () {
1539
- var res;
1540
- return __generator(this, function (_a) {
1541
- switch (_a.label) {
1542
- case 0: return [4 /*yield*/, agent.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(200)];
1543
- case 1:
1544
- res = _a.sent();
1545
- (0, bun_test_1.expect)(res.body.data.name).toBe("Kale");
1546
- (0, bun_test_1.expect)(res.body.data.calories).toBe(1);
1547
- (0, bun_test_1.expect)(res.body.data.hidden).toBe(false);
1548
- return [4 /*yield*/, agent
1549
- .patch("/food/".concat(spinach._id))
1550
- .send({ lastEatenWith: { dressing: "2023-12-03T00:00:20.000Z" } })
1551
- .expect(200)];
1552
- case 2:
1553
- // Update a Map field.
1554
- res = _a.sent();
1555
- (0, bun_test_1.expect)(res.body.data.name).toBe("Kale");
1556
- (0, bun_test_1.expect)(res.body.data.calories).toBe(1);
1557
- (0, bun_test_1.expect)(res.body.data.hidden).toBe(false);
1558
- (0, bun_test_1.expect)(res.body.data.lastEatenWith).toEqual({
1559
- dressing: "2023-12-03T00:00:20.000Z",
1560
- });
1561
- return [4 /*yield*/, agent
1562
- .patch("/food/".concat(spinach._id))
1563
- .send({
1564
- lastEatenWith: {
1565
- cucumber: "2023-12-04T12:00:20.000Z",
1566
- dressing: "2023-12-03T00:00:20.000Z",
1567
- },
1568
- })
1569
- .expect(200)];
1570
- case 3:
1571
- // Update a Map field.
1572
- res = _a.sent();
1573
- (0, bun_test_1.expect)(res.body.data.lastEatenWith).toEqual({
1574
- cucumber: "2023-12-04T12:00:20.000Z",
1575
- dressing: "2023-12-03T00:00:20.000Z",
1576
- });
1577
- return [2 /*return*/];
1578
- }
1579
- });
1580
- }); });
1581
- (0, bun_test_1.it)("update using dot notation", function () { return __awaiter(void 0, void 0, void 0, function () {
1582
- var res, dbSpinach;
1583
- return __generator(this, function (_a) {
1584
- switch (_a.label) {
1585
- case 0: return [4 /*yield*/, agent
1586
- .patch("/food/".concat(spinach._id))
1587
- .send({ "source.href": "https://food.com" })
1588
- .expect(200)];
1589
- case 1:
1590
- res = _a.sent();
1591
- // Assert the field was updated with dot notation.
1592
- (0, bun_test_1.expect)(res.body.data.source.href).toBe("https://food.com");
1593
- // Assert these fields haven't changed.
1594
- (0, bun_test_1.expect)(res.body.data.source.name).toBe("Brand");
1595
- (0, bun_test_1.expect)(res.body.data.source.dateAdded).toBe("2023-12-13T12:30:00.000Z");
1596
- return [4 /*yield*/, tests_1.FoodModel.findById(spinach._id)];
1597
- case 2:
1598
- dbSpinach = _a.sent();
1599
- (0, bun_test_1.expect)(dbSpinach === null || dbSpinach === void 0 ? void 0 : dbSpinach.source.href).toBe("https://food.com");
1600
- (0, bun_test_1.expect)(dbSpinach === null || dbSpinach === void 0 ? void 0 : dbSpinach.source.name).toBe("Brand");
1601
- (0, bun_test_1.expect)(dbSpinach === null || dbSpinach === void 0 ? void 0 : dbSpinach.source.dateAdded).toBe("2023-12-13T12:30:00.000Z");
1602
- return [2 /*return*/];
1603
- }
1604
- });
1605
- }); });
1606
- });
1607
- (0, bun_test_1.describe)("populate", function () {
1608
- var admin;
1609
- var notAdmin;
1610
- var agent;
1611
- var spinach;
1612
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
1613
- var _a, _b;
1614
- return __generator(this, function (_c) {
1615
- switch (_c.label) {
1616
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1617
- case 1:
1618
- _a = __read.apply(void 0, [_c.sent(), 2]), admin = _a[0], notAdmin = _a[1];
1619
- return [4 /*yield*/, Promise.all([
1620
- tests_1.FoodModel.create({
1621
- calories: 1,
1622
- created: new Date("2021-12-03T00:00:20.000Z"),
1623
- hidden: false,
1624
- name: "Spinach",
1625
- ownerId: admin._id,
1626
- source: {
1627
- name: "Brand",
1628
- },
1629
- }),
1630
- tests_1.FoodModel.create({
1631
- calories: 1,
1632
- created: new Date("2022-12-03T00:00:20.000Z"),
1633
- hidden: false,
1634
- name: "Carrots",
1635
- ownerId: notAdmin._id,
1636
- source: {
1637
- name: "User",
1638
- },
1639
- }),
1640
- ])];
1641
- case 2:
1642
- _b = __read.apply(void 0, [_c.sent(), 1]), spinach = _b[0];
1643
- app = (0, tests_1.getBaseServer)();
1644
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
1645
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
1646
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1647
- allowAnonymous: true,
1648
- permissions: {
1649
- create: [permissions_1.Permissions.IsAny],
1650
- delete: [permissions_1.Permissions.IsAny],
1651
- list: [permissions_1.Permissions.IsAny],
1652
- read: [permissions_1.Permissions.IsAny],
1653
- update: [permissions_1.Permissions.IsAny],
1654
- },
1655
- populatePaths: [{ fields: ["email"], path: "ownerId" }],
1656
- sort: "-created",
1657
- }));
1658
- server = (0, supertest_1.default)(app);
1659
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
1660
- case 3:
1661
- agent = _c.sent();
1662
- return [2 /*return*/];
1663
- }
1664
- });
1665
- }); });
1666
- (0, bun_test_1.it)("lists with populate", function () { return __awaiter(void 0, void 0, void 0, function () {
1667
- var res, _a, carrots, spin;
1668
- return __generator(this, function (_b) {
1669
- switch (_b.label) {
1670
- case 0: return [4 /*yield*/, agent.get("/food").expect(200)];
1671
- case 1:
1672
- res = _b.sent();
1673
- (0, bun_test_1.expect)(res.body.data).toHaveLength(2);
1674
- _a = __read(res.body.data, 2), carrots = _a[0], spin = _a[1];
1675
- (0, bun_test_1.expect)(carrots.ownerId._id).toBe(notAdmin._id.toString());
1676
- (0, bun_test_1.expect)(carrots.ownerId.email).toBe(notAdmin.email);
1677
- (0, bun_test_1.expect)(carrots.ownerId.name).toBeUndefined();
1678
- (0, bun_test_1.expect)(spin.ownerId._id).toBe(admin._id.toString());
1679
- (0, bun_test_1.expect)(spin.ownerId.email).toBe(admin.email);
1680
- (0, bun_test_1.expect)(spin.ownerId.name).toBeUndefined();
1681
- return [2 /*return*/];
1682
- }
1683
- });
1684
- }); });
1685
- (0, bun_test_1.it)("reads with populate", function () { return __awaiter(void 0, void 0, void 0, function () {
1686
- var res;
1687
- return __generator(this, function (_a) {
1688
- switch (_a.label) {
1689
- case 0: return [4 /*yield*/, agent.get("/food/".concat(spinach._id)).expect(200)];
1690
- case 1:
1691
- res = _a.sent();
1692
- (0, bun_test_1.expect)(res.body.data.ownerId._id).toBe(admin._id.toString());
1693
- (0, bun_test_1.expect)(res.body.data.ownerId.email).toBe(admin.email);
1694
- (0, bun_test_1.expect)(res.body.data.ownerId.name).toBeUndefined();
1695
- return [2 /*return*/];
1696
- }
1697
- });
1698
- }); });
1699
- (0, bun_test_1.it)("creates with populate", function () { return __awaiter(void 0, void 0, void 0, function () {
1700
- var res;
1701
- return __generator(this, function (_a) {
1702
- switch (_a.label) {
1703
- case 0: return [4 /*yield*/, server
1704
- .post("/food")
1705
- .send({
1706
- calories: 15,
1707
- name: "Broccoli",
1708
- ownerId: admin._id,
1709
- })
1710
- .expect(201)];
1711
- case 1:
1712
- res = _a.sent();
1713
- (0, bun_test_1.expect)(res.body.data.ownerId._id).toBe(admin._id.toString());
1714
- (0, bun_test_1.expect)(res.body.data.ownerId.email).toBe(admin.email);
1715
- (0, bun_test_1.expect)(res.body.data.ownerId.name).toBeUndefined();
1716
- return [2 /*return*/];
1717
- }
1718
- });
1719
- }); });
1720
- (0, bun_test_1.it)("updates with populate", function () { return __awaiter(void 0, void 0, void 0, function () {
1721
- var res;
1722
- return __generator(this, function (_a) {
1723
- switch (_a.label) {
1724
- case 0: return [4 /*yield*/, server
1725
- .patch("/food/".concat(spinach._id))
1726
- .send({
1727
- name: "NotSpinach",
1728
- })
1729
- .expect(200)];
1730
- case 1:
1731
- res = _a.sent();
1732
- (0, bun_test_1.expect)(res.body.data.ownerId._id).toBe(admin._id.toString());
1733
- (0, bun_test_1.expect)(res.body.data.ownerId.email).toBe(admin.email);
1734
- (0, bun_test_1.expect)(res.body.data.ownerId.name).toBeUndefined();
1735
- return [2 /*return*/];
1736
- }
1737
- });
1738
- }); });
1739
- });
1740
- (0, bun_test_1.describe)("responseHandler", function () {
1741
- var admin;
1742
- var agent;
1743
- var spinach;
1744
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
1745
- var _a, _b;
1746
- return __generator(this, function (_c) {
1747
- switch (_c.label) {
1748
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1749
- case 1:
1750
- _a = __read.apply(void 0, [_c.sent(), 1]), admin = _a[0];
1751
- return [4 /*yield*/, Promise.all([
1752
- tests_1.FoodModel.create({
1753
- calories: 1,
1754
- created: new Date("2021-12-03T00:00:20.000Z"),
1755
- hidden: false,
1756
- name: "Spinach",
1757
- ownerId: admin._id,
1758
- source: {
1759
- name: "Brand",
1760
- },
1761
- }),
1762
- tests_1.FoodModel.create({
1763
- calories: 100,
1764
- created: Date.now() - 10,
1765
- hidden: true,
1766
- name: "Apple",
1767
- ownerId: admin === null || admin === void 0 ? void 0 : admin._id,
1768
- }),
1769
- ])];
1770
- case 2:
1771
- _b = __read.apply(void 0, [_c.sent(), 1]), spinach = _b[0];
1772
- app = (0, tests_1.getBaseServer)();
1773
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
1774
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
1775
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1776
- allowAnonymous: true,
1777
- permissions: {
1778
- create: [permissions_1.Permissions.IsAny],
1779
- delete: [permissions_1.Permissions.IsAny],
1780
- list: [permissions_1.Permissions.IsAny],
1781
- read: [permissions_1.Permissions.IsAny],
1782
- update: [permissions_1.Permissions.IsAny],
1783
- },
1784
- responseHandler: function (data, method) {
1785
- if (method === "list") {
1786
- return data.map(function (d) { return ({
1787
- foo: "bar",
1788
- id: d._id,
1789
- }); });
1790
- }
1791
- return {
1792
- foo: "bar",
1793
- id: data._id,
1794
- };
1795
- },
1796
- }));
1797
- server = (0, supertest_1.default)(app);
1798
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
1799
- case 3:
1800
- agent = _c.sent();
1801
- return [2 /*return*/];
1802
- }
1803
- });
1804
- }); });
1805
- (0, bun_test_1.it)("reads with serialize", function () { return __awaiter(void 0, void 0, void 0, function () {
1806
- var res;
1807
- return __generator(this, function (_a) {
1808
- switch (_a.label) {
1809
- case 0: return [4 /*yield*/, agent.get("/food/".concat(spinach._id)).expect(200)];
1810
- case 1:
1811
- res = _a.sent();
1812
- (0, bun_test_1.expect)(res.body.data.ownerId).toBeUndefined();
1813
- (0, bun_test_1.expect)(res.body.data.id).toBe(spinach._id.toString());
1814
- (0, bun_test_1.expect)(res.body.data.foo).toBe("bar");
1815
- return [2 /*return*/];
1816
- }
1817
- });
1818
- }); });
1819
- (0, bun_test_1.it)("list with serialize", function () { return __awaiter(void 0, void 0, void 0, function () {
1820
- var res;
1821
- return __generator(this, function (_a) {
1822
- switch (_a.label) {
1823
- case 0: return [4 /*yield*/, agent.get("/food").expect(200)];
1824
- case 1:
1825
- res = _a.sent();
1826
- (0, bun_test_1.expect)(res.body.data[0].ownerId).toBeUndefined();
1827
- (0, bun_test_1.expect)(res.body.data[1].ownerId).toBeUndefined();
1828
- (0, bun_test_1.expect)(res.body.data[0].id).toBeDefined();
1829
- (0, bun_test_1.expect)(res.body.data[0].foo).toBe("bar");
1830
- (0, bun_test_1.expect)(res.body.data[1].id).toBeDefined();
1831
- (0, bun_test_1.expect)(res.body.data[1].foo).toBe("bar");
1832
- return [2 /*return*/];
1833
- }
1834
- });
1835
- }); });
1836
- });
1837
- (0, bun_test_1.describe)("plugins", function () {
1838
- var agent;
1839
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
1840
- return __generator(this, function (_a) {
1841
- switch (_a.label) {
1842
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1843
- case 1:
1844
- _a.sent();
1845
- app = (0, tests_1.getBaseServer)();
1846
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
1847
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
1848
- app.use("/users", (0, api_1.modelRouter)(tests_1.UserModel, {
1849
- allowAnonymous: true,
1850
- permissions: {
1851
- create: [permissions_1.Permissions.IsAny],
1852
- delete: [permissions_1.Permissions.IsAny],
1853
- list: [permissions_1.Permissions.IsAny],
1854
- read: [permissions_1.Permissions.IsAny],
1855
- update: [permissions_1.Permissions.IsAny],
1856
- },
1857
- }));
1858
- server = (0, supertest_1.default)(app);
1859
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
1860
- case 2:
1861
- agent = _a.sent();
1862
- return [2 /*return*/];
1863
- }
1864
- });
1865
- }); });
1866
- (0, bun_test_1.it)("check that security fields are filtered", function () { return __awaiter(void 0, void 0, void 0, function () {
1867
- var res;
1868
- return __generator(this, function (_a) {
1869
- switch (_a.label) {
1870
- case 0: return [4 /*yield*/, agent.get("/users").expect(200)];
1871
- case 1:
1872
- res = _a.sent();
1873
- (0, bun_test_1.expect)(res.body.data[0].email).toBeDefined();
1874
- (0, bun_test_1.expect)(res.body.data[0].token).toBeUndefined();
1875
- (0, bun_test_1.expect)(res.body.data[0].hash).toBeUndefined();
1876
- (0, bun_test_1.expect)(res.body.data[0].salt).toBeUndefined();
1877
- return [2 /*return*/];
1878
- }
1879
- });
1880
- }); });
1881
- });
1882
- (0, bun_test_1.describe)("error handling", function () {
1883
- var admin;
1884
- var agent;
1885
- var spinach;
1886
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
1887
- var _a;
1888
- return __generator(this, function (_b) {
1889
- switch (_b.label) {
1890
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1891
- case 1:
1892
- _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
1893
- return [4 /*yield*/, tests_1.FoodModel.create({
1894
- calories: 1,
1895
- created: new Date("2021-12-03T00:00:20.000Z"),
1896
- hidden: false,
1897
- name: "Spinach",
1898
- ownerId: admin._id,
1899
- source: {
1900
- name: "Brand",
1901
- },
1902
- })];
1903
- case 2:
1904
- spinach = _b.sent();
1905
- app = (0, tests_1.getBaseServer)();
1906
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
1907
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
1908
- return [2 /*return*/];
1909
- }
1910
- });
1911
- }); });
1912
- (0, bun_test_1.it)("PUT returns 500 not supported", function () { return __awaiter(void 0, void 0, void 0, function () {
1913
- var res;
1914
- return __generator(this, function (_a) {
1915
- switch (_a.label) {
1916
- case 0:
1917
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1918
- allowAnonymous: true,
1919
- permissions: {
1920
- create: [permissions_1.Permissions.IsAny],
1921
- delete: [permissions_1.Permissions.IsAny],
1922
- list: [permissions_1.Permissions.IsAny],
1923
- read: [permissions_1.Permissions.IsAny],
1924
- update: [permissions_1.Permissions.IsAny],
1925
- },
1926
- }));
1927
- server = (0, supertest_1.default)(app);
1928
- return [4 /*yield*/, server.put("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(500)];
1929
- case 1:
1930
- res = _a.sent();
1931
- (0, bun_test_1.expect)(res.body.title).toBe("PUT is not supported.");
1932
- return [2 /*return*/];
1933
- }
1934
- });
1935
- }); });
1936
- (0, bun_test_1.it)("preCreate returning undefined throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
1937
- var res;
1938
- return __generator(this, function (_a) {
1939
- switch (_a.label) {
1940
- case 0:
1941
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1942
- allowAnonymous: true,
1943
- permissions: {
1944
- create: [permissions_1.Permissions.IsAny],
1945
- delete: [permissions_1.Permissions.IsAny],
1946
- list: [permissions_1.Permissions.IsAny],
1947
- read: [permissions_1.Permissions.IsAny],
1948
- update: [permissions_1.Permissions.IsAny],
1949
- },
1950
- preCreate: function () { return undefined; },
1951
- }));
1952
- server = (0, supertest_1.default)(app);
1953
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(403)];
1954
- case 1:
1955
- res = _a.sent();
1956
- (0, bun_test_1.expect)(res.body.title).toBe("Create not allowed");
1957
- (0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preCreate");
1958
- return [2 /*return*/];
1959
- }
1960
- });
1961
- }); });
1962
- (0, bun_test_1.it)("preUpdate returning undefined throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
1963
- var res;
1964
- return __generator(this, function (_a) {
1965
- switch (_a.label) {
1966
- case 0:
1967
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1968
- allowAnonymous: true,
1969
- permissions: {
1970
- create: [permissions_1.Permissions.IsAny],
1971
- delete: [permissions_1.Permissions.IsAny],
1972
- list: [permissions_1.Permissions.IsAny],
1973
- read: [permissions_1.Permissions.IsAny],
1974
- update: [permissions_1.Permissions.IsAny],
1975
- },
1976
- preUpdate: function () { return undefined; },
1977
- }));
1978
- server = (0, supertest_1.default)(app);
1979
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(403)];
1980
- case 1:
1981
- res = _a.sent();
1982
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
1983
- (0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preUpdate");
1984
- return [2 /*return*/];
1985
- }
1986
- });
1987
- }); });
1988
- (0, bun_test_1.it)("preDelete returning undefined throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
1989
- var res;
1990
- return __generator(this, function (_a) {
1991
- switch (_a.label) {
1992
- case 0:
1993
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1994
- allowAnonymous: true,
1995
- permissions: {
1996
- create: [permissions_1.Permissions.IsAny],
1997
- delete: [permissions_1.Permissions.IsAny],
1998
- list: [permissions_1.Permissions.IsAny],
1999
- read: [permissions_1.Permissions.IsAny],
2000
- update: [permissions_1.Permissions.IsAny],
2001
- },
2002
- preDelete: function () { return undefined; },
2003
- }));
2004
- server = (0, supertest_1.default)(app);
2005
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
2006
- case 1:
2007
- agent = _a.sent();
2008
- return [4 /*yield*/, agent.delete("/food/".concat(spinach._id)).expect(403)];
2009
- case 2:
2010
- res = _a.sent();
2011
- (0, bun_test_1.expect)(res.body.title).toBe("Delete not allowed");
2012
- (0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preDelete");
2013
- return [2 /*return*/];
2014
- }
2015
- });
2016
- }); });
2017
- (0, bun_test_1.it)("postCreate hook error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2018
- var res;
2019
- return __generator(this, function (_a) {
2020
- switch (_a.label) {
2021
- case 0:
2022
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2023
- allowAnonymous: true,
2024
- permissions: {
2025
- create: [permissions_1.Permissions.IsAny],
2026
- delete: [permissions_1.Permissions.IsAny],
2027
- list: [permissions_1.Permissions.IsAny],
2028
- read: [permissions_1.Permissions.IsAny],
2029
- update: [permissions_1.Permissions.IsAny],
2030
- },
2031
- postCreate: function () {
2032
- throw new Error("postCreate failed");
2033
- },
2034
- }));
2035
- server = (0, supertest_1.default)(app);
2036
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
2037
- case 1:
2038
- res = _a.sent();
2039
- (0, bun_test_1.expect)(res.body.title).toContain("postCreate hook error");
2040
- return [2 /*return*/];
2041
- }
2042
- });
2043
- }); });
2044
- (0, bun_test_1.it)("postUpdate hook error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2045
- var res;
2046
- return __generator(this, function (_a) {
2047
- switch (_a.label) {
2048
- case 0:
2049
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2050
- allowAnonymous: true,
2051
- permissions: {
2052
- create: [permissions_1.Permissions.IsAny],
2053
- delete: [permissions_1.Permissions.IsAny],
2054
- list: [permissions_1.Permissions.IsAny],
2055
- read: [permissions_1.Permissions.IsAny],
2056
- update: [permissions_1.Permissions.IsAny],
2057
- },
2058
- postUpdate: function () {
2059
- throw new Error("postUpdate failed");
2060
- },
2061
- }));
2062
- server = (0, supertest_1.default)(app);
2063
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(400)];
2064
- case 1:
2065
- res = _a.sent();
2066
- (0, bun_test_1.expect)(res.body.title).toContain("postUpdate hook error");
2067
- return [2 /*return*/];
2068
- }
2069
- });
2070
- }); });
2071
- (0, bun_test_1.it)("postDelete hook error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2072
- var res;
2073
- return __generator(this, function (_a) {
2074
- switch (_a.label) {
2075
- case 0:
2076
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2077
- allowAnonymous: true,
2078
- permissions: {
2079
- create: [permissions_1.Permissions.IsAny],
2080
- delete: [permissions_1.Permissions.IsAny],
2081
- list: [permissions_1.Permissions.IsAny],
2082
- read: [permissions_1.Permissions.IsAny],
2083
- update: [permissions_1.Permissions.IsAny],
2084
- },
2085
- postDelete: function () {
2086
- throw new Error("postDelete failed");
2087
- },
2088
- }));
2089
- server = (0, supertest_1.default)(app);
2090
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
2091
- case 1:
2092
- agent = _a.sent();
2093
- return [4 /*yield*/, agent.delete("/food/".concat(spinach._id)).expect(400)];
2094
- case 2:
2095
- res = _a.sent();
2096
- (0, bun_test_1.expect)(res.body.title).toContain("postDelete hook error");
2097
- return [2 /*return*/];
2098
- }
2099
- });
2100
- }); });
2101
- (0, bun_test_1.it)("responseHandler error in read is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2102
- var res;
2103
- return __generator(this, function (_a) {
2104
- switch (_a.label) {
2105
- case 0:
2106
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2107
- allowAnonymous: true,
2108
- permissions: {
2109
- create: [permissions_1.Permissions.IsAny],
2110
- delete: [permissions_1.Permissions.IsAny],
2111
- list: [permissions_1.Permissions.IsAny],
2112
- read: [permissions_1.Permissions.IsAny],
2113
- update: [permissions_1.Permissions.IsAny],
2114
- },
2115
- responseHandler: function (_data, method) {
2116
- if (method === "read") {
2117
- throw new Error("responseHandler read failed");
2118
- }
2119
- return {};
2120
- },
2121
- }));
2122
- server = (0, supertest_1.default)(app);
2123
- return [4 /*yield*/, server.get("/food/".concat(spinach._id)).expect(500)];
2124
- case 1:
2125
- res = _a.sent();
2126
- (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2127
- return [2 /*return*/];
2128
- }
2129
- });
2130
- }); });
2131
- (0, bun_test_1.it)("responseHandler error in create is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2132
- var res;
2133
- return __generator(this, function (_a) {
2134
- switch (_a.label) {
2135
- case 0:
2136
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2137
- allowAnonymous: true,
2138
- permissions: {
2139
- create: [permissions_1.Permissions.IsAny],
2140
- delete: [permissions_1.Permissions.IsAny],
2141
- list: [permissions_1.Permissions.IsAny],
2142
- read: [permissions_1.Permissions.IsAny],
2143
- update: [permissions_1.Permissions.IsAny],
2144
- },
2145
- responseHandler: function (_data, method) {
2146
- if (method === "create") {
2147
- throw new Error("responseHandler create failed");
2148
- }
2149
- return {};
2150
- },
2151
- }));
2152
- server = (0, supertest_1.default)(app);
2153
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(500)];
2154
- case 1:
2155
- res = _a.sent();
2156
- (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2157
- return [2 /*return*/];
2158
- }
2159
- });
2160
- }); });
2161
- (0, bun_test_1.it)("responseHandler error in update is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2162
- var res;
2163
- return __generator(this, function (_a) {
2164
- switch (_a.label) {
2165
- case 0:
2166
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2167
- allowAnonymous: true,
2168
- permissions: {
2169
- create: [permissions_1.Permissions.IsAny],
2170
- delete: [permissions_1.Permissions.IsAny],
2171
- list: [permissions_1.Permissions.IsAny],
2172
- read: [permissions_1.Permissions.IsAny],
2173
- update: [permissions_1.Permissions.IsAny],
2174
- },
2175
- responseHandler: function (_data, method) {
2176
- if (method === "update") {
2177
- throw new Error("responseHandler update failed");
2178
- }
2179
- return {};
2180
- },
2181
- }));
2182
- server = (0, supertest_1.default)(app);
2183
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(500)];
2184
- case 1:
2185
- res = _a.sent();
2186
- (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2187
- return [2 /*return*/];
2188
- }
2189
- });
2190
- }); });
2191
- (0, bun_test_1.it)("responseHandler error in list is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2192
- var res;
2193
- return __generator(this, function (_a) {
2194
- switch (_a.label) {
2195
- case 0:
2196
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2197
- allowAnonymous: true,
2198
- permissions: {
2199
- create: [permissions_1.Permissions.IsAny],
2200
- delete: [permissions_1.Permissions.IsAny],
2201
- list: [permissions_1.Permissions.IsAny],
2202
- read: [permissions_1.Permissions.IsAny],
2203
- update: [permissions_1.Permissions.IsAny],
2204
- },
2205
- responseHandler: function (_data, method) {
2206
- if (method === "list") {
2207
- throw new Error("responseHandler list failed");
2208
- }
2209
- return {};
2210
- },
2211
- }));
2212
- server = (0, supertest_1.default)(app);
2213
- return [4 /*yield*/, server.get("/food").expect(500)];
2214
- case 1:
2215
- res = _a.sent();
2216
- (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2217
- return [2 /*return*/];
2218
- }
2219
- });
2220
- }); });
2221
- (0, bun_test_1.it)("list with non-array responseHandler returns data directly", function () { return __awaiter(void 0, void 0, void 0, function () {
2222
- var res;
2223
- return __generator(this, function (_a) {
2224
- switch (_a.label) {
2225
- case 0:
2226
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2227
- allowAnonymous: true,
2228
- permissions: {
2229
- create: [permissions_1.Permissions.IsAny],
2230
- delete: [permissions_1.Permissions.IsAny],
2231
- list: [permissions_1.Permissions.IsAny],
2232
- read: [permissions_1.Permissions.IsAny],
2233
- update: [permissions_1.Permissions.IsAny],
2234
- },
2235
- responseHandler: function (_data, method) {
2236
- if (method === "list") {
2237
- return { custom: "response" };
2238
- }
2239
- return {};
2240
- },
2241
- }));
2242
- server = (0, supertest_1.default)(app);
2243
- return [4 /*yield*/, server.get("/food").expect(200)];
2244
- case 1:
2245
- res = _a.sent();
2246
- (0, bun_test_1.expect)(res.body.data).toEqual({ custom: "response" });
2247
- (0, bun_test_1.expect)(res.body.more).toBeUndefined();
2248
- (0, bun_test_1.expect)(res.body.total).toBeUndefined();
2249
- return [2 /*return*/];
2250
- }
2251
- });
2252
- }); });
2253
- (0, bun_test_1.it)("list with query sort param", function () { return __awaiter(void 0, void 0, void 0, function () {
2254
- var res;
2255
- return __generator(this, function (_a) {
2256
- switch (_a.label) {
2257
- case 0: return [4 /*yield*/, tests_1.FoodModel.create({
2258
- calories: 200,
2259
- created: new Date("2021-12-04T00:00:20.000Z"),
2260
- hidden: false,
2261
- name: "Apple",
2262
- ownerId: admin._id,
2263
- })];
2264
- case 1:
2265
- _a.sent();
123
+ }),
124
+ tests_1.FoodModel.create({
125
+ calories: 1,
126
+ created: new Date("2022-12-03T00:00:20.000Z"),
127
+ hidden: false,
128
+ name: "Carrots",
129
+ ownerId: notAdmin._id,
130
+ source: {
131
+ name: "User",
132
+ },
133
+ }),
134
+ ])];
135
+ case 2:
136
+ _b = __read.apply(void 0, [_c.sent(), 1]), spinach = _b[0];
137
+ app = (0, tests_1.getBaseServer)();
138
+ (0, auth_1.setupAuth)(app, tests_1.UserModel);
139
+ (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
2266
140
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2267
141
  allowAnonymous: true,
2268
142
  permissions: {
@@ -2272,135 +146,126 @@ var transformers_1 = require("./transformers");
2272
146
  read: [permissions_1.Permissions.IsAny],
2273
147
  update: [permissions_1.Permissions.IsAny],
2274
148
  },
2275
- queryFields: ["name"],
149
+ populatePaths: [{ fields: ["email"], path: "ownerId" }],
150
+ sort: "-created",
2276
151
  }));
2277
152
  server = (0, supertest_1.default)(app);
2278
- return [4 /*yield*/, server.get("/food?sort=name").expect(200)];
2279
- case 2:
2280
- res = _a.sent();
2281
- (0, bun_test_1.expect)(res.body.data[0].name).toBe("Apple");
2282
- (0, bun_test_1.expect)(res.body.data[1].name).toBe("Spinach");
2283
- return [4 /*yield*/, server.get("/food?sort=-name").expect(200)];
153
+ return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
2284
154
  case 3:
2285
- // Sort by name descending
2286
- res = _a.sent();
2287
- (0, bun_test_1.expect)(res.body.data[0].name).toBe("Spinach");
2288
- (0, bun_test_1.expect)(res.body.data[1].name).toBe("Apple");
155
+ agent = _c.sent();
2289
156
  return [2 /*return*/];
2290
157
  }
2291
158
  });
2292
159
  }); });
2293
- (0, bun_test_1.it)("queryFilter error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2294
- var res;
2295
- return __generator(this, function (_a) {
2296
- switch (_a.label) {
2297
- case 0:
2298
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2299
- allowAnonymous: true,
2300
- permissions: {
2301
- create: [permissions_1.Permissions.IsAny],
2302
- delete: [permissions_1.Permissions.IsAny],
2303
- list: [permissions_1.Permissions.IsAny],
2304
- read: [permissions_1.Permissions.IsAny],
2305
- update: [permissions_1.Permissions.IsAny],
2306
- },
2307
- queryFilter: function () {
2308
- throw new Error("queryFilter failed");
2309
- },
2310
- }));
2311
- server = (0, supertest_1.default)(app);
2312
- return [4 /*yield*/, server.get("/food").expect(400)];
160
+ (0, bun_test_1.it)("lists with populate", function () { return __awaiter(void 0, void 0, void 0, function () {
161
+ var res, _a, carrots, spin;
162
+ return __generator(this, function (_b) {
163
+ switch (_b.label) {
164
+ case 0: return [4 /*yield*/, agent.get("/food").expect(200)];
2313
165
  case 1:
2314
- res = _a.sent();
2315
- (0, bun_test_1.expect)(res.body.title).toContain("Query filter error");
166
+ res = _b.sent();
167
+ (0, bun_test_1.expect)(res.body.data).toHaveLength(2);
168
+ _a = __read(res.body.data, 2), carrots = _a[0], spin = _a[1];
169
+ (0, bun_test_1.expect)(carrots.ownerId._id).toBe(notAdmin._id.toString());
170
+ (0, bun_test_1.expect)(carrots.ownerId.email).toBe(notAdmin.email);
171
+ (0, bun_test_1.expect)(carrots.ownerId.name).toBeUndefined();
172
+ (0, bun_test_1.expect)(spin.ownerId._id).toBe(admin._id.toString());
173
+ (0, bun_test_1.expect)(spin.ownerId.email).toBe(admin.email);
174
+ (0, bun_test_1.expect)(spin.ownerId.name).toBeUndefined();
2316
175
  return [2 /*return*/];
2317
176
  }
2318
177
  });
2319
178
  }); });
2320
- (0, bun_test_1.it)("custom endpoints take priority", function () { return __awaiter(void 0, void 0, void 0, function () {
179
+ (0, bun_test_1.it)("reads with populate", function () { return __awaiter(void 0, void 0, void 0, function () {
2321
180
  var res;
2322
181
  return __generator(this, function (_a) {
2323
182
  switch (_a.label) {
2324
- case 0:
2325
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2326
- allowAnonymous: true,
2327
- endpoints: function (router) {
2328
- router.get("/custom", function (_req, res) {
2329
- res.json({ custom: true });
2330
- });
2331
- },
2332
- permissions: {
2333
- create: [permissions_1.Permissions.IsAny],
2334
- delete: [permissions_1.Permissions.IsAny],
2335
- list: [permissions_1.Permissions.IsAny],
2336
- read: [permissions_1.Permissions.IsAny],
2337
- update: [permissions_1.Permissions.IsAny],
2338
- },
2339
- }));
2340
- server = (0, supertest_1.default)(app);
2341
- return [4 /*yield*/, server.get("/food/custom").expect(200)];
183
+ case 0: return [4 /*yield*/, agent.get("/food/".concat(spinach._id)).expect(200)];
2342
184
  case 1:
2343
185
  res = _a.sent();
2344
- (0, bun_test_1.expect)(res.body.custom).toBe(true);
186
+ (0, bun_test_1.expect)(res.body.data.ownerId._id).toBe(admin._id.toString());
187
+ (0, bun_test_1.expect)(res.body.data.ownerId.email).toBe(admin.email);
188
+ (0, bun_test_1.expect)(res.body.data.ownerId.name).toBeUndefined();
2345
189
  return [2 /*return*/];
2346
190
  }
2347
191
  });
2348
192
  }); });
2349
- (0, bun_test_1.it)("disallowed query param returns 400", function () { return __awaiter(void 0, void 0, void 0, function () {
193
+ (0, bun_test_1.it)("creates with populate", function () { return __awaiter(void 0, void 0, void 0, function () {
2350
194
  var res;
2351
195
  return __generator(this, function (_a) {
2352
196
  switch (_a.label) {
2353
- case 0:
2354
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2355
- allowAnonymous: true,
2356
- permissions: {
2357
- create: [permissions_1.Permissions.IsAny],
2358
- delete: [permissions_1.Permissions.IsAny],
2359
- list: [permissions_1.Permissions.IsAny],
2360
- read: [permissions_1.Permissions.IsAny],
2361
- update: [permissions_1.Permissions.IsAny],
2362
- },
2363
- queryFields: ["name"],
2364
- }));
2365
- server = (0, supertest_1.default)(app);
2366
- return [4 /*yield*/, server.get("/food?calories=100").expect(400)];
197
+ case 0: return [4 /*yield*/, server
198
+ .post("/food")
199
+ .send({
200
+ calories: 15,
201
+ name: "Broccoli",
202
+ ownerId: admin._id,
203
+ })
204
+ .expect(201)];
2367
205
  case 1:
2368
206
  res = _a.sent();
2369
- (0, bun_test_1.expect)(res.body.title).toContain("calories is not allowed as a query param");
207
+ (0, bun_test_1.expect)(res.body.data.ownerId._id).toBe(admin._id.toString());
208
+ (0, bun_test_1.expect)(res.body.data.ownerId.email).toBe(admin.email);
209
+ (0, bun_test_1.expect)(res.body.data.ownerId.name).toBeUndefined();
2370
210
  return [2 /*return*/];
2371
211
  }
2372
212
  });
2373
213
  }); });
2374
- (0, bun_test_1.it)("queryFilter returning null returns empty array", function () { return __awaiter(void 0, void 0, void 0, function () {
214
+ (0, bun_test_1.it)("updates with populate", function () { return __awaiter(void 0, void 0, void 0, function () {
2375
215
  var res;
2376
216
  return __generator(this, function (_a) {
2377
217
  switch (_a.label) {
2378
- case 0:
2379
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2380
- allowAnonymous: true,
2381
- permissions: {
2382
- create: [permissions_1.Permissions.IsAny],
2383
- delete: [permissions_1.Permissions.IsAny],
2384
- list: [permissions_1.Permissions.IsAny],
2385
- read: [permissions_1.Permissions.IsAny],
2386
- update: [permissions_1.Permissions.IsAny],
2387
- },
2388
- queryFilter: function () { return null; },
2389
- }));
2390
- server = (0, supertest_1.default)(app);
2391
- return [4 /*yield*/, server.get("/food").expect(200)];
218
+ case 0: return [4 /*yield*/, server
219
+ .patch("/food/".concat(spinach._id))
220
+ .send({
221
+ name: "NotSpinach",
222
+ })
223
+ .expect(200)];
2392
224
  case 1:
2393
225
  res = _a.sent();
2394
- (0, bun_test_1.expect)(res.body.data).toEqual([]);
226
+ (0, bun_test_1.expect)(res.body.data.ownerId._id).toBe(admin._id.toString());
227
+ (0, bun_test_1.expect)(res.body.data.ownerId.email).toBe(admin.email);
228
+ (0, bun_test_1.expect)(res.body.data.ownerId.name).toBeUndefined();
2395
229
  return [2 /*return*/];
2396
230
  }
2397
231
  });
2398
232
  }); });
2399
- (0, bun_test_1.it)("preUpdate returning null throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
2400
- var res;
2401
- return __generator(this, function (_a) {
2402
- switch (_a.label) {
2403
- case 0:
233
+ });
234
+ (0, bun_test_1.describe)("responseHandler", function () {
235
+ var admin;
236
+ var agent;
237
+ var spinach;
238
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
239
+ var _a, _b;
240
+ return __generator(this, function (_c) {
241
+ switch (_c.label) {
242
+ case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
243
+ case 1:
244
+ _a = __read.apply(void 0, [_c.sent(), 1]), admin = _a[0];
245
+ return [4 /*yield*/, Promise.all([
246
+ tests_1.FoodModel.create({
247
+ calories: 1,
248
+ created: new Date("2021-12-03T00:00:20.000Z"),
249
+ hidden: false,
250
+ name: "Spinach",
251
+ ownerId: admin._id,
252
+ source: {
253
+ name: "Brand",
254
+ },
255
+ }),
256
+ tests_1.FoodModel.create({
257
+ calories: 100,
258
+ created: Date.now() - 10,
259
+ hidden: true,
260
+ name: "Apple",
261
+ ownerId: admin === null || admin === void 0 ? void 0 : admin._id,
262
+ }),
263
+ ])];
264
+ case 2:
265
+ _b = __read.apply(void 0, [_c.sent(), 1]), spinach = _b[0];
266
+ app = (0, tests_1.getBaseServer)();
267
+ (0, auth_1.setupAuth)(app, tests_1.UserModel);
268
+ (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
2404
269
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2405
270
  allowAnonymous: true,
2406
271
  permissions: {
@@ -2410,76 +275,71 @@ var transformers_1 = require("./transformers");
2410
275
  read: [permissions_1.Permissions.IsAny],
2411
276
  update: [permissions_1.Permissions.IsAny],
2412
277
  },
2413
- preUpdate: function () { return null; },
278
+ responseHandler: function (data, method) {
279
+ if (method === "list") {
280
+ return data.map(function (d) { return ({
281
+ foo: "bar",
282
+ id: d._id,
283
+ }); });
284
+ }
285
+ return {
286
+ foo: "bar",
287
+ id: data._id,
288
+ };
289
+ },
2414
290
  }));
2415
291
  server = (0, supertest_1.default)(app);
2416
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(403)];
2417
- case 1:
2418
- res = _a.sent();
2419
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
292
+ return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
293
+ case 3:
294
+ agent = _c.sent();
2420
295
  return [2 /*return*/];
2421
296
  }
2422
297
  });
2423
298
  }); });
2424
- (0, bun_test_1.it)("preDelete returning null throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
299
+ (0, bun_test_1.it)("reads with serialize", function () { return __awaiter(void 0, void 0, void 0, function () {
2425
300
  var res;
2426
301
  return __generator(this, function (_a) {
2427
302
  switch (_a.label) {
2428
- case 0:
2429
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2430
- allowAnonymous: true,
2431
- permissions: {
2432
- create: [permissions_1.Permissions.IsAny],
2433
- delete: [permissions_1.Permissions.IsAny],
2434
- list: [permissions_1.Permissions.IsAny],
2435
- read: [permissions_1.Permissions.IsAny],
2436
- update: [permissions_1.Permissions.IsAny],
2437
- },
2438
- preDelete: function () { return null; },
2439
- }));
2440
- server = (0, supertest_1.default)(app);
2441
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
303
+ case 0: return [4 /*yield*/, agent.get("/food/".concat(spinach._id)).expect(200)];
2442
304
  case 1:
2443
- agent = _a.sent();
2444
- return [4 /*yield*/, agent.delete("/food/".concat(spinach._id)).expect(403)];
2445
- case 2:
2446
305
  res = _a.sent();
2447
- (0, bun_test_1.expect)(res.body.title).toBe("Delete not allowed");
306
+ (0, bun_test_1.expect)(res.body.data.ownerId).toBeUndefined();
307
+ (0, bun_test_1.expect)(res.body.data.id).toBe(spinach._id.toString());
308
+ (0, bun_test_1.expect)(res.body.data.foo).toBe("bar");
2448
309
  return [2 /*return*/];
2449
310
  }
2450
311
  });
2451
312
  }); });
2452
- (0, bun_test_1.it)("preCreate returning null throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
313
+ (0, bun_test_1.it)("list with serialize", function () { return __awaiter(void 0, void 0, void 0, function () {
2453
314
  var res;
2454
315
  return __generator(this, function (_a) {
2455
316
  switch (_a.label) {
2456
- case 0:
2457
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2458
- allowAnonymous: true,
2459
- permissions: {
2460
- create: [permissions_1.Permissions.IsAny],
2461
- delete: [permissions_1.Permissions.IsAny],
2462
- list: [permissions_1.Permissions.IsAny],
2463
- read: [permissions_1.Permissions.IsAny],
2464
- update: [permissions_1.Permissions.IsAny],
2465
- },
2466
- preCreate: function () { return null; },
2467
- }));
2468
- server = (0, supertest_1.default)(app);
2469
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(403)];
317
+ case 0: return [4 /*yield*/, agent.get("/food").expect(200)];
2470
318
  case 1:
2471
319
  res = _a.sent();
2472
- (0, bun_test_1.expect)(res.body.title).toBe("Create not allowed");
320
+ (0, bun_test_1.expect)(res.body.data[0].ownerId).toBeUndefined();
321
+ (0, bun_test_1.expect)(res.body.data[1].ownerId).toBeUndefined();
322
+ (0, bun_test_1.expect)(res.body.data[0].id).toBeDefined();
323
+ (0, bun_test_1.expect)(res.body.data[0].foo).toBe("bar");
324
+ (0, bun_test_1.expect)(res.body.data[1].id).toBeDefined();
325
+ (0, bun_test_1.expect)(res.body.data[1].foo).toBe("bar");
2473
326
  return [2 /*return*/];
2474
327
  }
2475
328
  });
2476
329
  }); });
2477
- (0, bun_test_1.it)("preCreate error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2478
- var res;
330
+ });
331
+ (0, bun_test_1.describe)("plugins", function () {
332
+ var agent;
333
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
2479
334
  return __generator(this, function (_a) {
2480
335
  switch (_a.label) {
2481
- case 0:
2482
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
336
+ case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
337
+ case 1:
338
+ _a.sent();
339
+ app = (0, tests_1.getBaseServer)();
340
+ (0, auth_1.setupAuth)(app, tests_1.UserModel);
341
+ (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
342
+ app.use("/users", (0, api_1.modelRouter)(tests_1.UserModel, {
2483
343
  allowAnonymous: true,
2484
344
  permissions: {
2485
345
  create: [permissions_1.Permissions.IsAny],
@@ -2488,56 +348,34 @@ var transformers_1 = require("./transformers");
2488
348
  read: [permissions_1.Permissions.IsAny],
2489
349
  update: [permissions_1.Permissions.IsAny],
2490
350
  },
2491
- preCreate: function () {
2492
- throw new Error("preCreate failed");
2493
- },
2494
351
  }));
2495
352
  server = (0, supertest_1.default)(app);
2496
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
2497
- case 1:
2498
- res = _a.sent();
2499
- (0, bun_test_1.expect)(res.body.title).toContain("preCreate hook error");
353
+ return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
354
+ case 2:
355
+ agent = _a.sent();
2500
356
  return [2 /*return*/];
2501
357
  }
2502
358
  });
2503
359
  }); });
2504
- (0, bun_test_1.it)("preUpdate error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
360
+ (0, bun_test_1.it)("check that security fields are filtered", function () { return __awaiter(void 0, void 0, void 0, function () {
2505
361
  var res;
2506
362
  return __generator(this, function (_a) {
2507
363
  switch (_a.label) {
2508
- case 0:
2509
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2510
- allowAnonymous: true,
2511
- permissions: {
2512
- create: [permissions_1.Permissions.IsAny],
2513
- delete: [permissions_1.Permissions.IsAny],
2514
- list: [permissions_1.Permissions.IsAny],
2515
- read: [permissions_1.Permissions.IsAny],
2516
- update: [permissions_1.Permissions.IsAny],
2517
- },
2518
- preUpdate: function () {
2519
- throw new Error("preUpdate failed");
2520
- },
2521
- }));
2522
- server = (0, supertest_1.default)(app);
2523
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(400)];
364
+ case 0: return [4 /*yield*/, agent.get("/users").expect(200)];
2524
365
  case 1:
2525
366
  res = _a.sent();
2526
- (0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook error");
367
+ (0, bun_test_1.expect)(res.body.data[0].email).toBeDefined();
368
+ (0, bun_test_1.expect)(res.body.data[0].token).toBeUndefined();
369
+ (0, bun_test_1.expect)(res.body.data[0].hash).toBeUndefined();
370
+ (0, bun_test_1.expect)(res.body.data[0].salt).toBeUndefined();
2527
371
  return [2 /*return*/];
2528
372
  }
2529
373
  });
2530
374
  }); });
2531
- (0, bun_test_1.it)("invalid array operation type returns 400", function () { return __awaiter(void 0, void 0, void 0, function () {
2532
- return __generator(this, function (_a) {
2533
- return [2 /*return*/];
2534
- });
2535
- }); });
2536
375
  });
2537
- (0, bun_test_1.describe)("array operation errors", function () {
376
+ (0, bun_test_1.describe)("error handling", function () {
2538
377
  var admin;
2539
- var apple;
2540
- var agent;
378
+ var spinach;
2541
379
  (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
2542
380
  var _a;
2543
381
  return __generator(this, function (_b) {
@@ -2546,19 +384,17 @@ var transformers_1 = require("./transformers");
2546
384
  case 1:
2547
385
  _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
2548
386
  return [4 /*yield*/, tests_1.FoodModel.create({
2549
- calories: 100,
2550
- categories: [
2551
- { name: "Fruit", show: true },
2552
- { name: "Popular", show: false },
2553
- ],
2554
- created: new Date("2021-12-03T00:00:30.000Z"),
387
+ calories: 1,
388
+ created: new Date("2021-12-03T00:00:20.000Z"),
2555
389
  hidden: false,
2556
- name: "Apple",
390
+ name: "Spinach",
2557
391
  ownerId: admin._id,
2558
- tags: ["healthy", "cheap"],
392
+ source: {
393
+ name: "Brand",
394
+ },
2559
395
  })];
2560
396
  case 2:
2561
- apple = _b.sent();
397
+ spinach = _b.sent();
2562
398
  app = (0, tests_1.getBaseServer)();
2563
399
  (0, auth_1.setupAuth)(app, tests_1.UserModel);
2564
400
  (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
@@ -2566,7 +402,7 @@ var transformers_1 = require("./transformers");
2566
402
  }
2567
403
  });
2568
404
  }); });
2569
- (0, bun_test_1.it)("array operation preUpdate returning undefined throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
405
+ (0, bun_test_1.it)("PUT returns 500 not supported", function () { return __awaiter(void 0, void 0, void 0, function () {
2570
406
  var res;
2571
407
  return __generator(this, function (_a) {
2572
408
  switch (_a.label) {
@@ -2574,28 +410,23 @@ var transformers_1 = require("./transformers");
2574
410
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2575
411
  allowAnonymous: true,
2576
412
  permissions: {
2577
- create: [permissions_1.Permissions.IsAdmin],
2578
- delete: [permissions_1.Permissions.IsAdmin],
2579
- list: [permissions_1.Permissions.IsAdmin],
2580
- read: [permissions_1.Permissions.IsAdmin],
2581
- update: [permissions_1.Permissions.IsAdmin],
413
+ create: [permissions_1.Permissions.IsAny],
414
+ delete: [permissions_1.Permissions.IsAny],
415
+ list: [permissions_1.Permissions.IsAny],
416
+ read: [permissions_1.Permissions.IsAny],
417
+ update: [permissions_1.Permissions.IsAny],
2582
418
  },
2583
- preUpdate: function () { return undefined; },
2584
419
  }));
2585
420
  server = (0, supertest_1.default)(app);
2586
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
421
+ return [4 /*yield*/, server.put("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(500)];
2587
422
  case 1:
2588
- agent = _a.sent();
2589
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
2590
- case 2:
2591
423
  res = _a.sent();
2592
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
2593
- (0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preUpdate");
424
+ (0, bun_test_1.expect)(res.body.title).toBe("PUT is not supported.");
2594
425
  return [2 /*return*/];
2595
426
  }
2596
427
  });
2597
428
  }); });
2598
- (0, bun_test_1.it)("array operation preUpdate returning null throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
429
+ (0, bun_test_1.it)("responseHandler error in read is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2599
430
  var res;
2600
431
  return __generator(this, function (_a) {
2601
432
  switch (_a.label) {
@@ -2603,27 +434,29 @@ var transformers_1 = require("./transformers");
2603
434
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2604
435
  allowAnonymous: true,
2605
436
  permissions: {
2606
- create: [permissions_1.Permissions.IsAdmin],
2607
- delete: [permissions_1.Permissions.IsAdmin],
2608
- list: [permissions_1.Permissions.IsAdmin],
2609
- read: [permissions_1.Permissions.IsAdmin],
2610
- update: [permissions_1.Permissions.IsAdmin],
437
+ create: [permissions_1.Permissions.IsAny],
438
+ delete: [permissions_1.Permissions.IsAny],
439
+ list: [permissions_1.Permissions.IsAny],
440
+ read: [permissions_1.Permissions.IsAny],
441
+ update: [permissions_1.Permissions.IsAny],
442
+ },
443
+ responseHandler: function (_data, method) {
444
+ if (method === "read") {
445
+ throw new Error("responseHandler read failed");
446
+ }
447
+ return {};
2611
448
  },
2612
- preUpdate: function () { return null; },
2613
449
  }));
2614
450
  server = (0, supertest_1.default)(app);
2615
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
451
+ return [4 /*yield*/, server.get("/food/".concat(spinach._id)).expect(500)];
2616
452
  case 1:
2617
- agent = _a.sent();
2618
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
2619
- case 2:
2620
453
  res = _a.sent();
2621
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
454
+ (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2622
455
  return [2 /*return*/];
2623
456
  }
2624
457
  });
2625
458
  }); });
2626
- (0, bun_test_1.it)("array operation preUpdate error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
459
+ (0, bun_test_1.it)("responseHandler error in create is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2627
460
  var res;
2628
461
  return __generator(this, function (_a) {
2629
462
  switch (_a.label) {
@@ -2631,29 +464,29 @@ var transformers_1 = require("./transformers");
2631
464
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2632
465
  allowAnonymous: true,
2633
466
  permissions: {
2634
- create: [permissions_1.Permissions.IsAdmin],
2635
- delete: [permissions_1.Permissions.IsAdmin],
2636
- list: [permissions_1.Permissions.IsAdmin],
2637
- read: [permissions_1.Permissions.IsAdmin],
2638
- update: [permissions_1.Permissions.IsAdmin],
467
+ create: [permissions_1.Permissions.IsAny],
468
+ delete: [permissions_1.Permissions.IsAny],
469
+ list: [permissions_1.Permissions.IsAny],
470
+ read: [permissions_1.Permissions.IsAny],
471
+ update: [permissions_1.Permissions.IsAny],
2639
472
  },
2640
- preUpdate: function () {
2641
- throw new Error("preUpdate array failed");
473
+ responseHandler: function (_data, method) {
474
+ if (method === "create") {
475
+ throw new Error("responseHandler create failed");
476
+ }
477
+ return {};
2642
478
  },
2643
479
  }));
2644
480
  server = (0, supertest_1.default)(app);
2645
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
481
+ return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(500)];
2646
482
  case 1:
2647
- agent = _a.sent();
2648
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(400)];
2649
- case 2:
2650
483
  res = _a.sent();
2651
- (0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook error");
484
+ (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2652
485
  return [2 /*return*/];
2653
486
  }
2654
487
  });
2655
488
  }); });
2656
- (0, bun_test_1.it)("array operation postUpdate error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
489
+ (0, bun_test_1.it)("responseHandler error in update is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2657
490
  var res;
2658
491
  return __generator(this, function (_a) {
2659
492
  switch (_a.label) {
@@ -2661,29 +494,29 @@ var transformers_1 = require("./transformers");
2661
494
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2662
495
  allowAnonymous: true,
2663
496
  permissions: {
2664
- create: [permissions_1.Permissions.IsAdmin],
2665
- delete: [permissions_1.Permissions.IsAdmin],
2666
- list: [permissions_1.Permissions.IsAdmin],
2667
- read: [permissions_1.Permissions.IsAdmin],
2668
- update: [permissions_1.Permissions.IsAdmin],
497
+ create: [permissions_1.Permissions.IsAny],
498
+ delete: [permissions_1.Permissions.IsAny],
499
+ list: [permissions_1.Permissions.IsAny],
500
+ read: [permissions_1.Permissions.IsAny],
501
+ update: [permissions_1.Permissions.IsAny],
2669
502
  },
2670
- postUpdate: function () {
2671
- throw new Error("postUpdate array failed");
503
+ responseHandler: function (_data, method) {
504
+ if (method === "update") {
505
+ throw new Error("responseHandler update failed");
506
+ }
507
+ return {};
2672
508
  },
2673
509
  }));
2674
510
  server = (0, supertest_1.default)(app);
2675
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
511
+ return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(500)];
2676
512
  case 1:
2677
- agent = _a.sent();
2678
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(400)];
2679
- case 2:
2680
513
  res = _a.sent();
2681
- (0, bun_test_1.expect)(res.body.title).toContain("PATCH Post Update error");
514
+ (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2682
515
  return [2 /*return*/];
2683
516
  }
2684
517
  });
2685
518
  }); });
2686
- (0, bun_test_1.it)("array operation denied without update permission", function () { return __awaiter(void 0, void 0, void 0, function () {
519
+ (0, bun_test_1.it)("responseHandler error in list is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2687
520
  var res;
2688
521
  return __generator(this, function (_a) {
2689
522
  switch (_a.label) {
@@ -2691,144 +524,100 @@ var transformers_1 = require("./transformers");
2691
524
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2692
525
  allowAnonymous: true,
2693
526
  permissions: {
2694
- create: [permissions_1.Permissions.IsAdmin],
2695
- delete: [permissions_1.Permissions.IsAdmin],
527
+ create: [permissions_1.Permissions.IsAny],
528
+ delete: [permissions_1.Permissions.IsAny],
2696
529
  list: [permissions_1.Permissions.IsAny],
2697
530
  read: [permissions_1.Permissions.IsAny],
2698
- update: [permissions_1.Permissions.IsAdmin],
531
+ update: [permissions_1.Permissions.IsAny],
2699
532
  },
2700
- }));
2701
- server = (0, supertest_1.default)(app);
2702
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
2703
- case 1:
2704
- agent = _a.sent();
2705
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(405)];
2706
- case 2:
2707
- res = _a.sent();
2708
- (0, bun_test_1.expect)(res.body.title).toContain("Access to PATCH");
2709
- return [2 /*return*/];
2710
- }
2711
- });
2712
- }); });
2713
- (0, bun_test_1.it)("array operation on non-existent document returns 404", function () { return __awaiter(void 0, void 0, void 0, function () {
2714
- var fakeId, res;
2715
- return __generator(this, function (_a) {
2716
- switch (_a.label) {
2717
- case 0:
2718
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2719
- allowAnonymous: true,
2720
- permissions: {
2721
- create: [permissions_1.Permissions.IsAdmin],
2722
- delete: [permissions_1.Permissions.IsAdmin],
2723
- list: [permissions_1.Permissions.IsAdmin],
2724
- read: [permissions_1.Permissions.IsAdmin],
2725
- update: [permissions_1.Permissions.IsAdmin],
533
+ responseHandler: function (_data, method) {
534
+ if (method === "list") {
535
+ throw new Error("responseHandler list failed");
536
+ }
537
+ return {};
2726
538
  },
2727
539
  }));
2728
540
  server = (0, supertest_1.default)(app);
2729
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
541
+ return [4 /*yield*/, server.get("/food").expect(500)];
2730
542
  case 1:
2731
- agent = _a.sent();
2732
- fakeId = "000000000000000000000000";
2733
- return [4 /*yield*/, agent.post("/food/".concat(fakeId, "/tags")).send({ tags: "organic" }).expect(404)];
2734
- case 2:
2735
543
  res = _a.sent();
2736
- (0, bun_test_1.expect)(res.body.title).toContain("Could not find document to PATCH");
544
+ (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2737
545
  return [2 /*return*/];
2738
546
  }
2739
547
  });
2740
548
  }); });
2741
- (0, bun_test_1.it)("array operation denied when user cannot update specific doc", function () { return __awaiter(void 0, void 0, void 0, function () {
549
+ (0, bun_test_1.it)("list with non-array responseHandler returns data directly", function () { return __awaiter(void 0, void 0, void 0, function () {
2742
550
  var res;
2743
551
  return __generator(this, function (_a) {
2744
552
  switch (_a.label) {
2745
553
  case 0:
2746
- // Create food owned by admin, then try to update as notAdmin
2747
554
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2748
555
  allowAnonymous: true,
2749
556
  permissions: {
2750
- create: [permissions_1.Permissions.IsAuthenticated],
2751
- delete: [permissions_1.Permissions.IsAuthenticated],
2752
- list: [permissions_1.Permissions.IsAuthenticated],
2753
- read: [permissions_1.Permissions.IsAuthenticated],
2754
- update: [permissions_1.Permissions.IsOwner],
557
+ create: [permissions_1.Permissions.IsAny],
558
+ delete: [permissions_1.Permissions.IsAny],
559
+ list: [permissions_1.Permissions.IsAny],
560
+ read: [permissions_1.Permissions.IsAny],
561
+ update: [permissions_1.Permissions.IsAny],
562
+ },
563
+ responseHandler: function (_data, method) {
564
+ if (method === "list") {
565
+ return { custom: "response" };
566
+ }
567
+ return {};
2755
568
  },
2756
569
  }));
2757
570
  server = (0, supertest_1.default)(app);
2758
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
571
+ return [4 /*yield*/, server.get("/food").expect(200)];
2759
572
  case 1:
2760
- // Login as notAdmin and try to update admin's food (apple)
2761
- agent = _a.sent();
2762
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
2763
- case 2:
2764
573
  res = _a.sent();
2765
- (0, bun_test_1.expect)(res.body.title).toContain("Patch not allowed");
574
+ (0, bun_test_1.expect)(res.body.data).toEqual({ custom: "response" });
575
+ (0, bun_test_1.expect)(res.body.more).toBeUndefined();
576
+ (0, bun_test_1.expect)(res.body.total).toBeUndefined();
2766
577
  return [2 /*return*/];
2767
578
  }
2768
579
  });
2769
580
  }); });
2770
- (0, bun_test_1.it)("array operation transform error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
581
+ (0, bun_test_1.it)("list with query sort param", function () { return __awaiter(void 0, void 0, void 0, function () {
2771
582
  var res;
2772
583
  return __generator(this, function (_a) {
2773
584
  switch (_a.label) {
2774
- case 0:
585
+ case 0: return [4 /*yield*/, tests_1.FoodModel.create({
586
+ calories: 200,
587
+ created: new Date("2021-12-04T00:00:20.000Z"),
588
+ hidden: false,
589
+ name: "Apple",
590
+ ownerId: admin._id,
591
+ })];
592
+ case 1:
593
+ _a.sent();
2775
594
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2776
595
  allowAnonymous: true,
2777
596
  permissions: {
2778
- create: [permissions_1.Permissions.IsAdmin],
2779
- delete: [permissions_1.Permissions.IsAdmin],
2780
- list: [permissions_1.Permissions.IsAdmin],
2781
- read: [permissions_1.Permissions.IsAdmin],
2782
- update: [permissions_1.Permissions.IsAdmin],
597
+ create: [permissions_1.Permissions.IsAny],
598
+ delete: [permissions_1.Permissions.IsAny],
599
+ list: [permissions_1.Permissions.IsAny],
600
+ read: [permissions_1.Permissions.IsAny],
601
+ update: [permissions_1.Permissions.IsAny],
2783
602
  },
2784
- transformer: (0, transformers_1.AdminOwnerTransformer)({
2785
- adminWriteFields: ["name"],
2786
- }),
603
+ queryFields: ["name"],
2787
604
  }));
2788
605
  server = (0, supertest_1.default)(app);
2789
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
2790
- case 1:
2791
- agent = _a.sent();
2792
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
606
+ return [4 /*yield*/, server.get("/food?sort=name").expect(200)];
2793
607
  case 2:
2794
608
  res = _a.sent();
2795
- (0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
2796
- return [2 /*return*/];
2797
- }
2798
- });
2799
- }); });
2800
- });
2801
- (0, bun_test_1.describe)("transformer errors", function () {
2802
- var admin;
2803
- var spinach;
2804
- var agent;
2805
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
2806
- var _a;
2807
- return __generator(this, function (_b) {
2808
- switch (_b.label) {
2809
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
2810
- case 1:
2811
- _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
2812
- return [4 /*yield*/, tests_1.FoodModel.create({
2813
- calories: 1,
2814
- created: new Date("2021-12-03T00:00:20.000Z"),
2815
- hidden: false,
2816
- name: "Spinach",
2817
- ownerId: admin._id,
2818
- source: {
2819
- name: "Brand",
2820
- },
2821
- })];
2822
- case 2:
2823
- spinach = _b.sent();
2824
- app = (0, tests_1.getBaseServer)();
2825
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
2826
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
609
+ (0, bun_test_1.expect)(res.body.data[0].name).toBe("Apple");
610
+ (0, bun_test_1.expect)(res.body.data[1].name).toBe("Spinach");
611
+ return [4 /*yield*/, server.get("/food?sort=-name").expect(200)];
612
+ case 3:
613
+ res = _a.sent();
614
+ (0, bun_test_1.expect)(res.body.data[0].name).toBe("Spinach");
615
+ (0, bun_test_1.expect)(res.body.data[1].name).toBe("Apple");
2827
616
  return [2 /*return*/];
2828
617
  }
2829
618
  });
2830
619
  }); });
2831
- (0, bun_test_1.it)("transform error in create is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
620
+ (0, bun_test_1.it)("queryFilter error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2832
621
  var res;
2833
622
  return __generator(this, function (_a) {
2834
623
  switch (_a.label) {
@@ -2842,27 +631,31 @@ var transformers_1 = require("./transformers");
2842
631
  read: [permissions_1.Permissions.IsAny],
2843
632
  update: [permissions_1.Permissions.IsAny],
2844
633
  },
2845
- transformer: (0, transformers_1.AdminOwnerTransformer)({
2846
- // Only allow 'name' to be written, so 'calories' will throw
2847
- anonWriteFields: ["name"],
2848
- }),
634
+ queryFilter: function () {
635
+ throw new Error("queryFilter failed");
636
+ },
2849
637
  }));
2850
638
  server = (0, supertest_1.default)(app);
2851
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
639
+ return [4 /*yield*/, server.get("/food").expect(400)];
2852
640
  case 1:
2853
641
  res = _a.sent();
2854
- (0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
642
+ (0, bun_test_1.expect)(res.body.title).toContain("Query filter error");
2855
643
  return [2 /*return*/];
2856
644
  }
2857
645
  });
2858
646
  }); });
2859
- (0, bun_test_1.it)("transform error in patch is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
647
+ (0, bun_test_1.it)("custom endpoints take priority", function () { return __awaiter(void 0, void 0, void 0, function () {
2860
648
  var res;
2861
649
  return __generator(this, function (_a) {
2862
650
  switch (_a.label) {
2863
651
  case 0:
2864
652
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2865
653
  allowAnonymous: true,
654
+ endpoints: function (router) {
655
+ router.get("/custom", function (_req, res) {
656
+ res.json({ custom: true });
657
+ });
658
+ },
2866
659
  permissions: {
2867
660
  create: [permissions_1.Permissions.IsAny],
2868
661
  delete: [permissions_1.Permissions.IsAny],
@@ -2870,28 +663,22 @@ var transformers_1 = require("./transformers");
2870
663
  read: [permissions_1.Permissions.IsAny],
2871
664
  update: [permissions_1.Permissions.IsAny],
2872
665
  },
2873
- transformer: (0, transformers_1.AdminOwnerTransformer)({
2874
- // Only allow 'name' to be written, so 'calories' will throw
2875
- anonWriteFields: ["name"],
2876
- }),
2877
666
  }));
2878
667
  server = (0, supertest_1.default)(app);
2879
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ calories: 100 }).expect(403)];
668
+ return [4 /*yield*/, server.get("/food/custom").expect(200)];
2880
669
  case 1:
2881
670
  res = _a.sent();
2882
- (0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
671
+ (0, bun_test_1.expect)(res.body.custom).toBe(true);
2883
672
  return [2 /*return*/];
2884
673
  }
2885
674
  });
2886
675
  }); });
2887
- (0, bun_test_1.it)("model.create validation error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2888
- var RequiredModel, res;
676
+ (0, bun_test_1.it)("disallowed query param returns 400", function () { return __awaiter(void 0, void 0, void 0, function () {
677
+ var res;
2889
678
  return __generator(this, function (_a) {
2890
679
  switch (_a.label) {
2891
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./tests")); })];
2892
- case 1:
2893
- RequiredModel = (_a.sent()).RequiredModel;
2894
- app.use("/required", (0, api_1.modelRouter)(RequiredModel, {
680
+ case 0:
681
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2895
682
  allowAnonymous: true,
2896
683
  permissions: {
2897
684
  create: [permissions_1.Permissions.IsAny],
@@ -2900,17 +687,18 @@ var transformers_1 = require("./transformers");
2900
687
  read: [permissions_1.Permissions.IsAny],
2901
688
  update: [permissions_1.Permissions.IsAny],
2902
689
  },
690
+ queryFields: ["name"],
2903
691
  }));
2904
692
  server = (0, supertest_1.default)(app);
2905
- return [4 /*yield*/, server.post("/required").send({ about: "test" }).expect(400)];
2906
- case 2:
693
+ return [4 /*yield*/, server.get("/food?calories=100").expect(400)];
694
+ case 1:
2907
695
  res = _a.sent();
2908
- (0, bun_test_1.expect)(res.body.title).toContain("Required");
696
+ (0, bun_test_1.expect)(res.body.title).toContain("calories is not allowed as a query param");
2909
697
  return [2 /*return*/];
2910
698
  }
2911
699
  });
2912
700
  }); });
2913
- (0, bun_test_1.it)("preDelete hook throwing APIError is re-thrown", function () { return __awaiter(void 0, void 0, void 0, function () {
701
+ (0, bun_test_1.it)("queryFilter returning null returns empty array", function () { return __awaiter(void 0, void 0, void 0, function () {
2914
702
  var res;
2915
703
  return __generator(this, function (_a) {
2916
704
  switch (_a.label) {
@@ -2924,30 +712,21 @@ var transformers_1 = require("./transformers");
2924
712
  read: [permissions_1.Permissions.IsAny],
2925
713
  update: [permissions_1.Permissions.IsAny],
2926
714
  },
2927
- preDelete: function () {
2928
- throw new errors_1.APIError({
2929
- disableExternalErrorTracking: true,
2930
- status: 400,
2931
- title: "Custom preDelete APIError",
2932
- });
2933
- },
715
+ queryFilter: function () { return null; },
2934
716
  }));
2935
717
  server = (0, supertest_1.default)(app);
2936
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
718
+ return [4 /*yield*/, server.get("/food").expect(200)];
2937
719
  case 1:
2938
- agent = _a.sent();
2939
- return [4 /*yield*/, agent.delete("/food/".concat(spinach._id)).expect(400)];
2940
- case 2:
2941
720
  res = _a.sent();
2942
- (0, bun_test_1.expect)(res.body.title).toBe("Custom preDelete APIError");
2943
- (0, bun_test_1.expect)(res.body.disableExternalErrorTracking).toBe(true);
721
+ (0, bun_test_1.expect)(res.body.data).toEqual([]);
2944
722
  return [2 /*return*/];
2945
723
  }
2946
724
  });
2947
725
  }); });
2948
726
  });
2949
- (0, bun_test_1.describe)("special query params", function () {
727
+ (0, bun_test_1.describe)("transformer errors", function () {
2950
728
  var admin;
729
+ var spinach;
2951
730
  (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
2952
731
  var _a;
2953
732
  return __generator(this, function (_b) {
@@ -2961,9 +740,12 @@ var transformers_1 = require("./transformers");
2961
740
  hidden: false,
2962
741
  name: "Spinach",
2963
742
  ownerId: admin._id,
743
+ source: {
744
+ name: "Brand",
745
+ },
2964
746
  })];
2965
747
  case 2:
2966
- _b.sent();
748
+ spinach = _b.sent();
2967
749
  app = (0, tests_1.getBaseServer)();
2968
750
  (0, auth_1.setupAuth)(app, tests_1.UserModel);
2969
751
  (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
@@ -2971,7 +753,7 @@ var transformers_1 = require("./transformers");
2971
753
  }
2972
754
  });
2973
755
  }); });
2974
- (0, bun_test_1.it)("period query param is stripped from query", function () { return __awaiter(void 0, void 0, void 0, function () {
756
+ (0, bun_test_1.it)("transform error in create is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2975
757
  var res;
2976
758
  return __generator(this, function (_a) {
2977
759
  switch (_a.label) {
@@ -2985,68 +767,24 @@ var transformers_1 = require("./transformers");
2985
767
  read: [permissions_1.Permissions.IsAny],
2986
768
  update: [permissions_1.Permissions.IsAny],
2987
769
  },
2988
- queryFields: ["name", "period"],
2989
- queryFilter: function (_user, query) {
2990
- // Simulate a queryFilter that accepts and processes period
2991
- if (query === null || query === void 0 ? void 0 : query.period) {
2992
- // Period is processed but shouldn't be passed to mongo
2993
- return query;
2994
- }
2995
- return query !== null && query !== void 0 ? query : {};
2996
- },
770
+ transformer: (0, transformers_1.AdminOwnerTransformer)({
771
+ anonWriteFields: ["name"],
772
+ }),
2997
773
  }));
2998
774
  server = (0, supertest_1.default)(app);
2999
- return [4 /*yield*/, server.get("/food?period=weekly").expect(200)];
3000
- case 1:
3001
- res = _a.sent();
3002
- (0, bun_test_1.expect)(res.body.data).toBeDefined();
3003
- return [2 /*return*/];
3004
- }
3005
- });
3006
- }); });
3007
- (0, bun_test_1.it)("query with false value", function () { return __awaiter(void 0, void 0, void 0, function () {
3008
- var res;
3009
- return __generator(this, function (_a) {
3010
- switch (_a.label) {
3011
- case 0:
3012
- // Create a food that is hidden
3013
- return [4 /*yield*/, tests_1.FoodModel.create({
3014
- calories: 50,
3015
- created: new Date("2021-12-04T00:00:20.000Z"),
3016
- hidden: true,
3017
- name: "HiddenFood",
3018
- ownerId: admin._id,
3019
- })];
775
+ return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
3020
776
  case 1:
3021
- // Create a food that is hidden
3022
- _a.sent();
3023
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3024
- allowAnonymous: true,
3025
- permissions: {
3026
- create: [permissions_1.Permissions.IsAny],
3027
- delete: [permissions_1.Permissions.IsAny],
3028
- list: [permissions_1.Permissions.IsAny],
3029
- read: [permissions_1.Permissions.IsAny],
3030
- update: [permissions_1.Permissions.IsAny],
3031
- },
3032
- queryFields: ["name", "hidden"],
3033
- }));
3034
- server = (0, supertest_1.default)(app);
3035
- return [4 /*yield*/, server.get("/food?hidden=false").expect(200)];
3036
- case 2:
3037
777
  res = _a.sent();
3038
- (0, bun_test_1.expect)(res.body.data.every(function (f) { return f.hidden === false; })).toBe(true);
778
+ (0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
3039
779
  return [2 /*return*/];
3040
780
  }
3041
781
  });
3042
782
  }); });
3043
- (0, bun_test_1.it)("$search query triggers special handling code path", function () { return __awaiter(void 0, void 0, void 0, function () {
783
+ (0, bun_test_1.it)("transform error in patch is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
3044
784
  var res;
3045
785
  return __generator(this, function (_a) {
3046
786
  switch (_a.label) {
3047
787
  case 0:
3048
- // The $search code path just accesses the collection but doesn't do anything with it
3049
- // This test verifies the code path is exercised
3050
788
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3051
789
  allowAnonymous: true,
3052
790
  permissions: {
@@ -3056,26 +794,25 @@ var transformers_1 = require("./transformers");
3056
794
  read: [permissions_1.Permissions.IsAny],
3057
795
  update: [permissions_1.Permissions.IsAny],
3058
796
  },
3059
- // Need to include $search in queryFields for it to pass validation
3060
- queryFields: ["name", "$search"],
797
+ transformer: (0, transformers_1.AdminOwnerTransformer)({
798
+ anonWriteFields: ["name"],
799
+ }),
3061
800
  }));
3062
801
  server = (0, supertest_1.default)(app);
3063
- return [4 /*yield*/, server.get("/food?$search=test")];
802
+ return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ calories: 100 }).expect(403)];
3064
803
  case 1:
3065
804
  res = _a.sent();
3066
- // May return 500 because $search is passed to Mongo which doesn't support it without Atlas
3067
- // The important thing is we've exercised the code path
3068
- (0, bun_test_1.expect)(res.status === 200 || res.status === 500).toBe(true);
805
+ (0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
3069
806
  return [2 /*return*/];
3070
807
  }
3071
808
  });
3072
809
  }); });
3073
- (0, bun_test_1.it)("$autocomplete query triggers special handling code path", function () { return __awaiter(void 0, void 0, void 0, function () {
810
+ (0, bun_test_1.it)("model.create validation error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
3074
811
  var res;
3075
812
  return __generator(this, function (_a) {
3076
813
  switch (_a.label) {
3077
814
  case 0:
3078
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
815
+ app.use("/required", (0, api_1.modelRouter)(tests_1.RequiredModel, {
3079
816
  allowAnonymous: true,
3080
817
  permissions: {
3081
818
  create: [permissions_1.Permissions.IsAny],
@@ -3084,13 +821,12 @@ var transformers_1 = require("./transformers");
3084
821
  read: [permissions_1.Permissions.IsAny],
3085
822
  update: [permissions_1.Permissions.IsAny],
3086
823
  },
3087
- queryFields: ["name", "$autocomplete"],
3088
824
  }));
3089
825
  server = (0, supertest_1.default)(app);
3090
- return [4 /*yield*/, server.get("/food?$autocomplete=test")];
826
+ return [4 /*yield*/, server.post("/required").send({ about: "test" }).expect(400)];
3091
827
  case 1:
3092
828
  res = _a.sent();
3093
- (0, bun_test_1.expect)(res.status === 200 || res.status === 500).toBe(true);
829
+ (0, bun_test_1.expect)(res.body.title).toContain("Required");
3094
830
  return [2 /*return*/];
3095
831
  }
3096
832
  });
@@ -3137,7 +873,6 @@ var transformers_1 = require("./transformers");
3137
873
  { fields: ["email"], path: "ownerId" },
3138
874
  { fields: ["name"], path: "eatenBy" },
3139
875
  ]);
3140
- // The result should be a query with populate applied
3141
876
  (0, bun_test_1.expect)(result).toBeDefined();
3142
877
  return [2 /*return*/];
3143
878
  }
@@ -3166,9 +901,6 @@ var transformers_1 = require("./transformers");
3166
901
  return __generator(this, function (_a) {
3167
902
  switch (_a.label) {
3168
903
  case 0:
3169
- // UserModel has the isDisabledPlugin which adds a 'disabled' field,
3170
- // but we need to test the 'deleted' field check.
3171
- // Let's use a model that has the deleted field.
3172
904
  app.use("/users", (0, api_1.modelRouter)(tests_1.UserModel, {
3173
905
  allowAnonymous: true,
3174
906
  permissions: {
@@ -3226,7 +958,6 @@ var transformers_1 = require("./transformers");
3226
958
  return __generator(this, function (_a) {
3227
959
  switch (_a.label) {
3228
960
  case 0:
3229
- // Test that valid populate works in create flow
3230
961
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3231
962
  allowAnonymous: true,
3232
963
  permissions: {
@@ -3246,7 +977,6 @@ var transformers_1 = require("./transformers");
3246
977
  case 1:
3247
978
  res = _a.sent();
3248
979
  (0, bun_test_1.expect)(res.body.data.name).toBe("Broccoli");
3249
- // Verify populate worked - ownerId should be an object with email
3250
980
  (0, bun_test_1.expect)(res.body.data.ownerId.email).toBe(admin.email);
3251
981
  return [2 /*return*/];
3252
982
  }
@@ -3287,7 +1017,6 @@ var transformers_1 = require("./transformers");
3287
1017
  return __generator(this, function (_a) {
3288
1018
  switch (_a.label) {
3289
1019
  case 0:
3290
- // The FoodModel has strict: "throw" which will cause validation errors for unknown fields
3291
1020
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3292
1021
  allowAnonymous: true,
3293
1022
  permissions: {
@@ -3330,7 +1059,6 @@ var transformers_1 = require("./transformers");
3330
1059
  return __generator(this, function (_a) {
3331
1060
  switch (_a.label) {
3332
1061
  case 0:
3333
- // Create a transformer that returns undefined
3334
1062
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3335
1063
  allowAnonymous: true,
3336
1064
  permissions: {
@@ -3389,10 +1117,8 @@ var transformers_1 = require("./transformers");
3389
1117
  catch (_b) {
3390
1118
  SoftDeleteModel = mongoose.model("SoftDeleteTest", softDeleteSchema);
3391
1119
  }
3392
- // Clean up any existing documents
3393
1120
  return [4 /*yield*/, SoftDeleteModel.deleteMany({})];
3394
1121
  case 2:
3395
- // Clean up any existing documents
3396
1122
  _a.sent();
3397
1123
  return [4 /*yield*/, SoftDeleteModel.create({ name: "TestItem" })];
3398
1124
  case 3:
@@ -3411,615 +1137,17 @@ var transformers_1 = require("./transformers");
3411
1137
  return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
3412
1138
  case 4:
3413
1139
  agent = _a.sent();
3414
- // Delete should soft delete (set deleted: true) instead of hard delete
3415
1140
  return [4 /*yield*/, agent.delete("/softdelete/".concat(testDoc._id)).expect(204)];
3416
1141
  case 5:
3417
- // Delete should soft delete (set deleted: true) instead of hard delete
3418
1142
  _a.sent();
3419
1143
  return [4 /*yield*/, SoftDeleteModel.findById(testDoc._id)];
3420
1144
  case 6:
3421
1145
  softDeleted = _a.sent();
3422
1146
  (0, bun_test_1.expect)(softDeleted).not.toBeNull();
3423
1147
  (0, bun_test_1.expect)(softDeleted === null || softDeleted === void 0 ? void 0 : softDeleted.deleted).toBe(true);
3424
- // Clean up
3425
1148
  return [4 /*yield*/, SoftDeleteModel.deleteMany({})];
3426
1149
  case 7:
3427
- // Clean up
3428
- _a.sent();
3429
- return [2 /*return*/];
3430
- }
3431
- });
3432
- }); });
3433
- });
3434
- (0, bun_test_1.describe)("array operation with undefined preUpdate return", function () {
3435
- var admin;
3436
- var apple;
3437
- var agent;
3438
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
3439
- var _a;
3440
- return __generator(this, function (_b) {
3441
- switch (_b.label) {
3442
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
3443
- case 1:
3444
- _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
3445
- return [4 /*yield*/, tests_1.FoodModel.create({
3446
- calories: 100,
3447
- categories: [
3448
- { name: "Fruit", show: true },
3449
- { name: "Popular", show: false },
3450
- ],
3451
- created: new Date("2021-12-03T00:00:30.000Z"),
3452
- hidden: false,
3453
- name: "Apple",
3454
- ownerId: admin._id,
3455
- tags: ["healthy", "cheap"],
3456
- })];
3457
- case 2:
3458
- apple = _b.sent();
3459
- app = (0, tests_1.getBaseServer)();
3460
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
3461
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
3462
- return [2 /*return*/];
3463
- }
3464
- });
3465
- }); });
3466
- (0, bun_test_1.it)("array operation preUpdate returning undefined for array POST throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
3467
- var res;
3468
- return __generator(this, function (_a) {
3469
- switch (_a.label) {
3470
- case 0:
3471
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3472
- allowAnonymous: true,
3473
- permissions: {
3474
- create: [permissions_1.Permissions.IsAdmin],
3475
- delete: [permissions_1.Permissions.IsAdmin],
3476
- list: [permissions_1.Permissions.IsAdmin],
3477
- read: [permissions_1.Permissions.IsAdmin],
3478
- update: [permissions_1.Permissions.IsAdmin],
3479
- },
3480
- preUpdate: function () { return undefined; },
3481
- }));
3482
- server = (0, supertest_1.default)(app);
3483
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
3484
- case 1:
3485
- agent = _a.sent();
3486
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
3487
- case 2:
3488
- res = _a.sent();
3489
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
3490
- (0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preUpdate");
3491
- return [2 /*return*/];
3492
- }
3493
- });
3494
- }); });
3495
- (0, bun_test_1.it)("array operation preUpdate returning null for array PATCH throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
3496
- var res;
3497
- return __generator(this, function (_a) {
3498
- switch (_a.label) {
3499
- case 0:
3500
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3501
- allowAnonymous: true,
3502
- permissions: {
3503
- create: [permissions_1.Permissions.IsAdmin],
3504
- delete: [permissions_1.Permissions.IsAdmin],
3505
- list: [permissions_1.Permissions.IsAdmin],
3506
- read: [permissions_1.Permissions.IsAdmin],
3507
- update: [permissions_1.Permissions.IsAdmin],
3508
- },
3509
- preUpdate: function () { return null; },
3510
- }));
3511
- server = (0, supertest_1.default)(app);
3512
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
3513
- case 1:
3514
- agent = _a.sent();
3515
- return [4 /*yield*/, agent
3516
- .patch("/food/".concat(apple._id, "/tags/healthy"))
3517
- .send({ tags: "unhealthy" })
3518
- .expect(403)];
3519
- case 2:
3520
- res = _a.sent();
3521
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
3522
- return [2 /*return*/];
3523
- }
3524
- });
3525
- }); });
3526
- (0, bun_test_1.it)("array operation preUpdate error for array DELETE is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
3527
- var res;
3528
- return __generator(this, function (_a) {
3529
- switch (_a.label) {
3530
- case 0:
3531
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3532
- allowAnonymous: true,
3533
- permissions: {
3534
- create: [permissions_1.Permissions.IsAdmin],
3535
- delete: [permissions_1.Permissions.IsAdmin],
3536
- list: [permissions_1.Permissions.IsAdmin],
3537
- read: [permissions_1.Permissions.IsAdmin],
3538
- update: [permissions_1.Permissions.IsAdmin],
3539
- },
3540
- preUpdate: function () {
3541
- throw new Error("preUpdate error during delete");
3542
- },
3543
- }));
3544
- server = (0, supertest_1.default)(app);
3545
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
3546
- case 1:
3547
- agent = _a.sent();
3548
- return [4 /*yield*/, agent.delete("/food/".concat(apple._id, "/tags/healthy")).expect(400)];
3549
- case 2:
3550
- res = _a.sent();
3551
- (0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook error");
3552
- return [2 /*return*/];
3553
- }
3554
- });
3555
- }); });
3556
- });
3557
- });
3558
- (0, bun_test_1.describe)("errors module", function () {
3559
- (0, bun_test_1.describe)("APIError", function () {
3560
- (0, bun_test_1.it)("sets default status to 500 when not provided", function () {
3561
- var error = new errors_1.APIError({ title: "Test error" });
3562
- (0, bun_test_1.expect)(error.status).toBe(500);
3563
- });
3564
- (0, bun_test_1.it)("sets status to 500 for invalid status codes below 400", function () {
3565
- var error = new errors_1.APIError({ status: 200, title: "Test error" });
3566
- (0, bun_test_1.expect)(error.status).toBe(500);
3567
- });
3568
- (0, bun_test_1.it)("sets status to 500 for invalid status codes above 599", function () {
3569
- var error = new errors_1.APIError({ status: 600, title: "Test error" });
3570
- (0, bun_test_1.expect)(error.status).toBe(500);
3571
- });
3572
- (0, bun_test_1.it)("includes error stack in message when error is provided", function () {
3573
- var originalError = new Error("Original error");
3574
- var apiError = new errors_1.APIError({
3575
- error: originalError,
3576
- title: "Wrapped error",
3577
- });
3578
- (0, bun_test_1.expect)(apiError.message).toContain("Wrapped error");
3579
- (0, bun_test_1.expect)(originalError.stack).toBeDefined();
3580
- (0, bun_test_1.expect)(apiError.message).toContain(originalError.stack);
3581
- });
3582
- (0, bun_test_1.it)("includes detail in message when provided", function () {
3583
- var error = new errors_1.APIError({
3584
- detail: "More details here",
3585
- title: "Test error",
3586
- });
3587
- (0, bun_test_1.expect)(error.message).toContain("Test error");
3588
- (0, bun_test_1.expect)(error.message).toContain("More details here");
3589
- });
3590
- (0, bun_test_1.it)("sets fields in meta when provided", function () {
3591
- var _a;
3592
- var error = new errors_1.APIError({
3593
- fields: { email: "Invalid email format" },
3594
- title: "Validation error",
3595
- });
3596
- (0, bun_test_1.expect)((_a = error.meta) === null || _a === void 0 ? void 0 : _a.fields).toEqual({ email: "Invalid email format" });
3597
- });
3598
- });
3599
- (0, bun_test_1.describe)("errorsPlugin", function () {
3600
- (0, bun_test_1.it)("adds apiErrors field to schema", function () { return __awaiter(void 0, void 0, void 0, function () {
3601
- var mongoose, errorsPlugin, testSchema;
3602
- return __generator(this, function (_a) {
3603
- switch (_a.label) {
3604
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("mongoose")); })];
3605
- case 1:
3606
- mongoose = _a.sent();
3607
- return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./errors")); })];
3608
- case 2:
3609
- errorsPlugin = (_a.sent()).errorsPlugin;
3610
- testSchema = new mongoose.Schema({ name: String });
3611
- errorsPlugin(testSchema);
3612
- (0, bun_test_1.expect)(testSchema.path("apiErrors")).toBeDefined();
3613
- return [2 /*return*/];
3614
- }
3615
- });
3616
- }); });
3617
- });
3618
- (0, bun_test_1.describe)("isAPIError", function () {
3619
- (0, bun_test_1.it)("returns true for APIError instances", function () {
3620
- var isAPIError = require("./errors").isAPIError;
3621
- var error = new errors_1.APIError({ title: "Test" });
3622
- (0, bun_test_1.expect)(isAPIError(error)).toBe(true);
3623
- });
3624
- (0, bun_test_1.it)("returns false for regular Error instances", function () {
3625
- var isAPIError = require("./errors").isAPIError;
3626
- var error = new Error("Test");
3627
- (0, bun_test_1.expect)(isAPIError(error)).toBe(false);
3628
- });
3629
- });
3630
- (0, bun_test_1.describe)("getDisableExternalErrorTracking", function () {
3631
- (0, bun_test_1.it)("returns undefined for non-objects", function () {
3632
- var getDisableExternalErrorTracking = require("./errors").getDisableExternalErrorTracking;
3633
- (0, bun_test_1.expect)(getDisableExternalErrorTracking(null)).toBeUndefined();
3634
- (0, bun_test_1.expect)(getDisableExternalErrorTracking("string")).toBeUndefined();
3635
- });
3636
- (0, bun_test_1.it)("returns value from APIError", function () {
3637
- var getDisableExternalErrorTracking = require("./errors").getDisableExternalErrorTracking;
3638
- var error = new errors_1.APIError({ disableExternalErrorTracking: true, title: "Test" });
3639
- (0, bun_test_1.expect)(getDisableExternalErrorTracking(error)).toBe(true);
3640
- });
3641
- (0, bun_test_1.it)("returns value from plain object with property", function () {
3642
- var getDisableExternalErrorTracking = require("./errors").getDisableExternalErrorTracking;
3643
- var obj = { disableExternalErrorTracking: true };
3644
- (0, bun_test_1.expect)(getDisableExternalErrorTracking(obj)).toBe(true);
3645
- });
3646
- });
3647
- (0, bun_test_1.describe)("getAPIErrorBody", function () {
3648
- (0, bun_test_1.it)("includes all non-undefined fields", function () {
3649
- var getAPIErrorBody = require("./errors").getAPIErrorBody;
3650
- var error = new errors_1.APIError({
3651
- code: "TEST_CODE",
3652
- detail: "Test detail",
3653
- id: "error-123",
3654
- links: { about: "http://example.com" },
3655
- meta: { extra: "data" },
3656
- source: { parameter: "id" },
3657
- status: 400,
3658
- title: "Test error",
3659
- });
3660
- var body = getAPIErrorBody(error);
3661
- (0, bun_test_1.expect)(body.title).toBe("Test error");
3662
- (0, bun_test_1.expect)(body.status).toBe(400);
3663
- (0, bun_test_1.expect)(body.code).toBe("TEST_CODE");
3664
- (0, bun_test_1.expect)(body.detail).toBe("Test detail");
3665
- (0, bun_test_1.expect)(body.id).toBe("error-123");
3666
- (0, bun_test_1.expect)(body.links).toEqual({ about: "http://example.com" });
3667
- (0, bun_test_1.expect)(body.source).toEqual({ parameter: "id" });
3668
- (0, bun_test_1.expect)(body.meta).toEqual({ extra: "data" });
3669
- });
3670
- });
3671
- (0, bun_test_1.describe)("apiUnauthorizedMiddleware", function () {
3672
- (0, bun_test_1.it)("returns 401 for Unauthorized errors", function () {
3673
- var apiUnauthorizedMiddleware = require("./errors").apiUnauthorizedMiddleware;
3674
- var err = new Error("Unauthorized");
3675
- var res = {
3676
- json: function (data) {
3677
- this.body = data;
3678
- return this;
3679
- },
3680
- send: function () {
3681
- return this;
3682
- },
3683
- status: function (code) {
3684
- this.statusCode = code;
3685
- return this;
3686
- },
3687
- };
3688
- var next = function () { };
3689
- apiUnauthorizedMiddleware(err, {}, res, next);
3690
- (0, bun_test_1.expect)(res.statusCode).toBe(401);
3691
- (0, bun_test_1.expect)(res.body.title).toBe("Unauthorized");
3692
- });
3693
- (0, bun_test_1.it)("calls next for non-Unauthorized errors", function () {
3694
- var apiUnauthorizedMiddleware = require("./errors").apiUnauthorizedMiddleware;
3695
- var err = new Error("Some other error");
3696
- var nextCalled = false;
3697
- var next = function () {
3698
- nextCalled = true;
3699
- };
3700
- apiUnauthorizedMiddleware(err, {}, {}, next);
3701
- (0, bun_test_1.expect)(nextCalled).toBe(true);
3702
- });
3703
- });
3704
- });
3705
- (0, bun_test_1.describe)("permissions module", function () {
3706
- (0, bun_test_1.describe)("OwnerQueryFilter", function () {
3707
- (0, bun_test_1.it)("returns ownerId filter when user is provided", function () {
3708
- var OwnerQueryFilter = require("./permissions").OwnerQueryFilter;
3709
- var user = { id: "user-123" };
3710
- var filter = OwnerQueryFilter(user);
3711
- (0, bun_test_1.expect)(filter).toEqual({ ownerId: "user-123" });
3712
- });
3713
- (0, bun_test_1.it)("returns null when user is undefined", function () {
3714
- var OwnerQueryFilter = require("./permissions").OwnerQueryFilter;
3715
- var filter = OwnerQueryFilter(undefined);
3716
- (0, bun_test_1.expect)(filter).toBeNull();
3717
- });
3718
- });
3719
- (0, bun_test_1.describe)("Permissions.IsAuthenticatedOrReadOnly", function () {
3720
- (0, bun_test_1.it)("returns true for authenticated non-anonymous users", function () {
3721
- var Permissions = require("./permissions").Permissions;
3722
- var user = { id: "user-123", isAnonymous: false };
3723
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("create", user)).toBe(true);
3724
- });
3725
- (0, bun_test_1.it)("returns true for read methods when user is anonymous", function () {
3726
- var Permissions = require("./permissions").Permissions;
3727
- var user = { id: "user-123", isAnonymous: true };
3728
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("list", user)).toBe(true);
3729
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("read", user)).toBe(true);
3730
- });
3731
- (0, bun_test_1.it)("returns false for write methods when user is anonymous", function () {
3732
- var Permissions = require("./permissions").Permissions;
3733
- var user = { id: "user-123", isAnonymous: true };
3734
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("create", user)).toBe(false);
3735
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("update", user)).toBe(false);
3736
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("delete", user)).toBe(false);
3737
- });
3738
- });
3739
- (0, bun_test_1.describe)("Permissions.IsOwnerOrReadOnly", function () {
3740
- (0, bun_test_1.it)("returns true when no object is provided", function () {
3741
- var Permissions = require("./permissions").Permissions;
3742
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("update", { id: "user-123" }, undefined)).toBe(true);
3743
- });
3744
- (0, bun_test_1.it)("returns true for admin users", function () {
3745
- var Permissions = require("./permissions").Permissions;
3746
- var user = { admin: true, id: "admin-123" };
3747
- var obj = { ownerId: "other-user" };
3748
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("update", user, obj)).toBe(true);
3749
- });
3750
- (0, bun_test_1.it)("returns true when user is owner", function () {
3751
- var Permissions = require("./permissions").Permissions;
3752
- var user = { id: "user-123" };
3753
- var obj = { ownerId: "user-123" };
3754
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("update", user, obj)).toBe(true);
3755
- });
3756
- (0, bun_test_1.it)("returns true for read methods when not owner", function () {
3757
- var Permissions = require("./permissions").Permissions;
3758
- var user = { id: "user-123" };
3759
- var obj = { ownerId: "other-user" };
3760
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("list", user, obj)).toBe(true);
3761
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("read", user, obj)).toBe(true);
3762
- });
3763
- (0, bun_test_1.it)("returns false for write methods when not owner", function () {
3764
- var Permissions = require("./permissions").Permissions;
3765
- var user = { id: "user-123" };
3766
- var obj = { ownerId: "other-user" };
3767
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("update", user, obj)).toBe(false);
3768
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("delete", user, obj)).toBe(false);
3769
- });
3770
- });
3771
- });
3772
- (0, bun_test_1.describe)("utils module", function () {
3773
- (0, bun_test_1.describe)("isValidObjectId", function () {
3774
- (0, bun_test_1.it)("returns true for valid ObjectId strings", function () {
3775
- var isValidObjectId = require("./utils").isValidObjectId;
3776
- (0, bun_test_1.expect)(isValidObjectId("507f1f77bcf86cd799439011")).toBe(true);
3777
- });
3778
- (0, bun_test_1.it)("returns false for invalid ObjectId strings", function () {
3779
- var isValidObjectId = require("./utils").isValidObjectId;
3780
- (0, bun_test_1.expect)(isValidObjectId("invalid-id")).toBe(false);
3781
- (0, bun_test_1.expect)(isValidObjectId("12345")).toBe(false);
3782
- (0, bun_test_1.expect)(isValidObjectId("")).toBe(false);
3783
- });
3784
- (0, bun_test_1.it)("returns false for 12-character strings that are not valid ObjectIds", function () {
3785
- var isValidObjectId = require("./utils").isValidObjectId;
3786
- // mongoose's native isValid returns true for any 12-char string
3787
- // but our implementation should return false since toString won't match
3788
- (0, bun_test_1.expect)(isValidObjectId("123456789012")).toBe(false);
3789
- });
3790
- });
3791
- (0, bun_test_1.describe)("timeout", function () {
3792
- (0, bun_test_1.it)("resolves after specified time", function () { return __awaiter(void 0, void 0, void 0, function () {
3793
- var timeout, start, elapsed;
3794
- return __generator(this, function (_a) {
3795
- switch (_a.label) {
3796
- case 0:
3797
- timeout = require("./utils").timeout;
3798
- start = Date.now();
3799
- return [4 /*yield*/, timeout(50)];
3800
- case 1:
3801
1150
  _a.sent();
3802
- elapsed = Date.now() - start;
3803
- (0, bun_test_1.expect)(elapsed).toBeGreaterThanOrEqual(40);
3804
- return [2 /*return*/];
3805
- }
3806
- });
3807
- }); });
3808
- });
3809
- // Note: Comprehensive checkModelsStrict tests are in utils.test.ts with mocked mongoose
3810
- });
3811
- (0, bun_test_1.describe)("populate module", function () {
3812
- (0, bun_test_1.describe)("unpopulate", function () {
3813
- (0, bun_test_1.it)("throws error when path is empty", function () { return __awaiter(void 0, void 0, void 0, function () {
3814
- var unpopulate, doc;
3815
- return __generator(this, function (_a) {
3816
- switch (_a.label) {
3817
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3818
- case 1:
3819
- unpopulate = (_a.sent()).unpopulate;
3820
- doc = { name: "test" };
3821
- (0, bun_test_1.expect)(function () { return unpopulate(doc, ""); }).toThrow("path is required");
3822
- return [2 /*return*/];
3823
- }
3824
- });
3825
- }); });
3826
- (0, bun_test_1.it)("unpopulates single populated field", function () { return __awaiter(void 0, void 0, void 0, function () {
3827
- var unpopulate, doc, result;
3828
- return __generator(this, function (_a) {
3829
- switch (_a.label) {
3830
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3831
- case 1:
3832
- unpopulate = (_a.sent()).unpopulate;
3833
- doc = {
3834
- name: "test",
3835
- ownerId: { _id: "owner-123", email: "owner@test.com" },
3836
- };
3837
- result = unpopulate(doc, "ownerId");
3838
- (0, bun_test_1.expect)(result.ownerId).toBe("owner-123");
3839
- return [2 /*return*/];
3840
- }
3841
- });
3842
- }); });
3843
- (0, bun_test_1.it)("unpopulates array of populated fields", function () { return __awaiter(void 0, void 0, void 0, function () {
3844
- var unpopulate, doc, result;
3845
- return __generator(this, function (_a) {
3846
- switch (_a.label) {
3847
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3848
- case 1:
3849
- unpopulate = (_a.sent()).unpopulate;
3850
- doc = {
3851
- items: [{ _id: "item-1", name: "Item 1" }, { _id: "item-2", name: "Item 2" }, "item-3"],
3852
- name: "test",
3853
- };
3854
- result = unpopulate(doc, "items");
3855
- (0, bun_test_1.expect)(result.items).toEqual(["item-1", "item-2", "item-3"]);
3856
- return [2 /*return*/];
3857
- }
3858
- });
3859
- }); });
3860
- (0, bun_test_1.it)("handles nested paths", function () { return __awaiter(void 0, void 0, void 0, function () {
3861
- var unpopulate, doc, result;
3862
- return __generator(this, function (_a) {
3863
- switch (_a.label) {
3864
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3865
- case 1:
3866
- unpopulate = (_a.sent()).unpopulate;
3867
- doc = {
3868
- name: "test",
3869
- nested: {
3870
- items: [
3871
- { _id: "item-1", name: "Item 1" },
3872
- { _id: "item-2", name: "Item 2" },
3873
- ],
3874
- },
3875
- };
3876
- result = unpopulate(doc, "nested.items");
3877
- (0, bun_test_1.expect)(result.nested.items).toEqual(["item-1", "item-2"]);
3878
- return [2 /*return*/];
3879
- }
3880
- });
3881
- }); });
3882
- (0, bun_test_1.it)("returns original doc when path does not exist", function () { return __awaiter(void 0, void 0, void 0, function () {
3883
- var unpopulate, doc, result;
3884
- return __generator(this, function (_a) {
3885
- switch (_a.label) {
3886
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3887
- case 1:
3888
- unpopulate = (_a.sent()).unpopulate;
3889
- doc = { name: "test" };
3890
- result = unpopulate(doc, "nonexistent");
3891
- (0, bun_test_1.expect)(result).toEqual(doc);
3892
- return [2 /*return*/];
3893
- }
3894
- });
3895
- }); });
3896
- (0, bun_test_1.it)("handles nested array paths", function () { return __awaiter(void 0, void 0, void 0, function () {
3897
- var unpopulate, doc, result;
3898
- return __generator(this, function (_a) {
3899
- switch (_a.label) {
3900
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3901
- case 1:
3902
- unpopulate = (_a.sent()).unpopulate;
3903
- doc = {
3904
- containers: [
3905
- { items: [{ _id: "item-1" }, { _id: "item-2" }] },
3906
- { items: [{ _id: "item-3" }, { _id: "item-4" }] },
3907
- ],
3908
- name: "test",
3909
- };
3910
- result = unpopulate(doc, "containers.items");
3911
- (0, bun_test_1.expect)(result.containers[0].items).toEqual(["item-1", "item-2"]);
3912
- (0, bun_test_1.expect)(result.containers[1].items).toEqual(["item-3", "item-4"]);
3913
- return [2 /*return*/];
3914
- }
3915
- });
3916
- }); });
3917
- });
3918
- });
3919
- (0, bun_test_1.describe)("auth module edge cases", function () {
3920
- (0, bun_test_1.describe)("generateTokens", function () {
3921
- (0, bun_test_1.it)("returns null tokens when user is missing", function () { return __awaiter(void 0, void 0, void 0, function () {
3922
- var generateTokens, result;
3923
- return __generator(this, function (_a) {
3924
- switch (_a.label) {
3925
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./auth")); })];
3926
- case 1:
3927
- generateTokens = (_a.sent()).generateTokens;
3928
- return [4 /*yield*/, generateTokens(null)];
3929
- case 2:
3930
- result = _a.sent();
3931
- (0, bun_test_1.expect)(result.token).toBeNull();
3932
- (0, bun_test_1.expect)(result.refreshToken).toBeNull();
3933
- return [2 /*return*/];
3934
- }
3935
- });
3936
- }); });
3937
- (0, bun_test_1.it)("returns null tokens when user has no _id", function () { return __awaiter(void 0, void 0, void 0, function () {
3938
- var generateTokens, result;
3939
- return __generator(this, function (_a) {
3940
- switch (_a.label) {
3941
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./auth")); })];
3942
- case 1:
3943
- generateTokens = (_a.sent()).generateTokens;
3944
- return [4 /*yield*/, generateTokens({ email: "test@test.com" })];
3945
- case 2:
3946
- result = _a.sent();
3947
- (0, bun_test_1.expect)(result.token).toBeNull();
3948
- (0, bun_test_1.expect)(result.refreshToken).toBeNull();
3949
- return [2 /*return*/];
3950
- }
3951
- });
3952
- }); });
3953
- (0, bun_test_1.it)("includes custom payload from generateJWTPayload option", function () { return __awaiter(void 0, void 0, void 0, function () {
3954
- var generateTokens, jwt, user, result, decoded;
3955
- return __generator(this, function (_a) {
3956
- switch (_a.label) {
3957
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./auth")); })];
3958
- case 1:
3959
- generateTokens = (_a.sent()).generateTokens;
3960
- return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("jsonwebtoken")); })];
3961
- case 2:
3962
- jwt = _a.sent();
3963
- user = { _id: "user-123" };
3964
- return [4 /*yield*/, generateTokens(user, {
3965
- generateJWTPayload: function (u) { return ({ customField: "customValue", userId: u._id }); },
3966
- })];
3967
- case 3:
3968
- result = _a.sent();
3969
- (0, bun_test_1.expect)(result.token).toBeDefined();
3970
- decoded = jwt.decode(result.token);
3971
- (0, bun_test_1.expect)(decoded.customField).toBe("customValue");
3972
- (0, bun_test_1.expect)(decoded.id).toBe("user-123");
3973
- return [2 /*return*/];
3974
- }
3975
- });
3976
- }); });
3977
- (0, bun_test_1.it)("uses custom token expiration from generateTokenExpiration option", function () { return __awaiter(void 0, void 0, void 0, function () {
3978
- var generateTokens, jwt, user, result, decoded, expectedExp;
3979
- return __generator(this, function (_a) {
3980
- switch (_a.label) {
3981
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./auth")); })];
3982
- case 1:
3983
- generateTokens = (_a.sent()).generateTokens;
3984
- return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("jsonwebtoken")); })];
3985
- case 2:
3986
- jwt = _a.sent();
3987
- user = { _id: "user-123" };
3988
- return [4 /*yield*/, generateTokens(user, {
3989
- generateTokenExpiration: function () { return "1h"; },
3990
- })];
3991
- case 3:
3992
- result = _a.sent();
3993
- (0, bun_test_1.expect)(result.token).toBeDefined();
3994
- decoded = jwt.decode(result.token);
3995
- expectedExp = Math.floor(Date.now() / 1000) + 3600;
3996
- (0, bun_test_1.expect)(decoded.exp).toBeGreaterThan(expectedExp - 5);
3997
- (0, bun_test_1.expect)(decoded.exp).toBeLessThan(expectedExp + 5);
3998
- return [2 /*return*/];
3999
- }
4000
- });
4001
- }); });
4002
- (0, bun_test_1.it)("uses custom refresh token expiration from generateRefreshTokenExpiration option", function () { return __awaiter(void 0, void 0, void 0, function () {
4003
- var generateTokens, jwt, user, result, decoded, expectedExp;
4004
- return __generator(this, function (_a) {
4005
- switch (_a.label) {
4006
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./auth")); })];
4007
- case 1:
4008
- generateTokens = (_a.sent()).generateTokens;
4009
- return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("jsonwebtoken")); })];
4010
- case 2:
4011
- jwt = _a.sent();
4012
- user = { _id: "user-123" };
4013
- return [4 /*yield*/, generateTokens(user, {
4014
- generateRefreshTokenExpiration: function () { return "7d"; },
4015
- })];
4016
- case 3:
4017
- result = _a.sent();
4018
- (0, bun_test_1.expect)(result.refreshToken).toBeDefined();
4019
- decoded = jwt.decode(result.refreshToken);
4020
- expectedExp = Math.floor(Date.now() / 1000) + 7 * 24 * 3600;
4021
- (0, bun_test_1.expect)(decoded.exp).toBeGreaterThan(expectedExp - 10);
4022
- (0, bun_test_1.expect)(decoded.exp).toBeLessThan(expectedExp + 10);
4023
1151
  return [2 /*return*/];
4024
1152
  }
4025
1153
  });