@terreno/api 0.0.11 → 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,979 +89,65 @@ 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 sortBy_1 = __importDefault(require("lodash/sortBy"));
94
- var qs_1 = __importDefault(require("qs"));
95
92
  var supertest_1 = __importDefault(require("supertest"));
96
93
  var api_1 = require("./api");
97
94
  var auth_1 = require("./auth");
98
- var errors_1 = require("./errors");
99
- var expressServer_1 = require("./expressServer");
100
95
  var permissions_1 = require("./permissions");
101
96
  var tests_1 = require("./tests");
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 () {
106
- var agent;
107
- (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) {
110
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
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];
579
- return [4 /*yield*/, Promise.all([
580
- tests_1.FoodModel.create({
581
- calories: 1,
582
- created: new Date("2021-12-03T00:00:20.000Z"),
583
- hidden: false,
584
- name: "Spinach",
585
- ownerId: admin._id,
586
- source: {
587
- name: "Brand",
588
- },
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;
101
+ (0, bun_test_1.describe)("populate", function () {
981
102
  var admin;
982
- var adminOther;
103
+ var notAdmin;
983
104
  var agent;
984
105
  var spinach;
985
- var apple;
986
- var carrots;
987
- var pizza;
988
106
  (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
989
- var results;
990
107
  var _a, _b;
991
108
  return __generator(this, function (_c) {
992
109
  switch (_c.label) {
993
110
  case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
994
111
  case 1:
995
- _a = __read.apply(void 0, [_c.sent(), 3]), admin = _a[0], notAdmin = _a[1], adminOther = _a[2];
112
+ _a = __read.apply(void 0, [_c.sent(), 2]), admin = _a[0], notAdmin = _a[1];
996
113
  return [4 /*yield*/, Promise.all([
997
114
  tests_1.FoodModel.create({
998
115
  calories: 1,
999
116
  created: new Date("2021-12-03T00:00:20.000Z"),
1000
- eatenBy: [admin._id],
1001
117
  hidden: false,
1002
- lastEatenWith: {
1003
- dressing: new Date("2021-12-03T19:00:30.000Z"),
1004
- },
1005
118
  name: "Spinach",
1006
- ownerId: notAdmin._id,
119
+ ownerId: admin._id,
1007
120
  source: {
1008
- dateAdded: "2023-12-13T12:30:00.000Z",
1009
- href: "https://www.google.com",
1010
121
  name: "Brand",
1011
122
  },
1012
123
  }),
1013
124
  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],
125
+ calories: 1,
126
+ created: new Date("2022-12-03T00:00:20.000Z"),
1025
127
  hidden: false,
1026
128
  name: "Carrots",
1027
- ownerId: admin._id,
129
+ ownerId: notAdmin._id,
1028
130
  source: {
1029
- name: "USDA",
131
+ name: "User",
1030
132
  },
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
133
  }),
1042
134
  ])];
1043
135
  case 2:
1044
- results = (_c.sent());
1045
- _b = __read(results, 4), spinach = _b[0], apple = _b[1], carrots = _b[2], pizza = _b[3];
136
+ _b = __read.apply(void 0, [_c.sent(), 1]), spinach = _b[0];
1046
137
  app = (0, tests_1.getBaseServer)();
1047
138
  (0, auth_1.setupAuth)(app, tests_1.UserModel);
1048
139
  (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
1049
- app.use(expressServer_1.logRequests);
1050
140
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1051
141
  allowAnonymous: true,
1052
- defaultLimit: 2,
1053
- defaultQueryParams: { hidden: false },
1054
- maxLimit: 3,
1055
142
  permissions: {
1056
- create: [permissions_1.Permissions.IsAuthenticated],
1057
- delete: [permissions_1.Permissions.IsAdmin],
143
+ create: [permissions_1.Permissions.IsAny],
144
+ delete: [permissions_1.Permissions.IsAny],
1058
145
  list: [permissions_1.Permissions.IsAny],
1059
146
  read: [permissions_1.Permissions.IsAny],
1060
- update: [permissions_1.Permissions.IsOwner],
147
+ update: [permissions_1.Permissions.IsAny],
1061
148
  },
1062
- populatePaths: [{ path: "ownerId" }],
1063
- queryFields: ["hidden", "name", "calories", "created", "source.name", "tags", "eatenBy"],
1064
- sort: { created: "descending" },
149
+ populatePaths: [{ fields: ["email"], path: "ownerId" }],
150
+ sort: "-created",
1065
151
  }));
1066
152
  server = (0, supertest_1.default)(app);
1067
153
  return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
@@ -1071,578 +157,471 @@ var tests_1 = require("./tests");
1071
157
  }
1072
158
  });
1073
159
  }); });
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) {
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) {
1096
164
  case 0: return [4 /*yield*/, agent.get("/food").expect(200)];
1097
165
  case 1:
1098
- res = _a.sent();
166
+ res = _b.sent();
1099
167
  (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);
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();
1210
175
  return [2 /*return*/];
1211
176
  }
1212
177
  });
1213
178
  }); });
1214
- (0, bun_test_1.it)("list query params", 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 () {
1215
180
  var res;
1216
181
  return __generator(this, function (_a) {
1217
182
  switch (_a.label) {
1218
- case 0: return [4 /*yield*/, agent.get("/food?hidden=true").expect(200)];
183
+ case 0: return [4 /*yield*/, agent.get("/food/".concat(spinach._id)).expect(200)];
1219
184
  case 1:
1220
185
  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);
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();
1225
189
  return [2 /*return*/];
1226
190
  }
1227
191
  });
1228
192
  }); });
1229
- (0, bun_test_1.it)("list query params not in list", 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 () {
1230
194
  var res;
1231
195
  return __generator(this, function (_a) {
1232
196
  switch (_a.label) {
1233
- case 0: return [4 /*yield*/, agent.get("/food?ownerId=".concat(admin._id)).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)];
1234
205
  case 1:
1235
206
  res = _a.sent();
1236
- (0, bun_test_1.expect)(res.body.title).toBe("ownerId 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();
1237
210
  return [2 /*return*/];
1238
211
  }
1239
212
  });
1240
213
  }); });
1241
- (0, bun_test_1.it)("list query by nested param", 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 () {
1242
215
  var res;
1243
216
  return __generator(this, function (_a) {
1244
217
  switch (_a.label) {
1245
- case 0: return [4 /*yield*/, agent.get("/food?source.name=USDA").expect(200)];
218
+ case 0: return [4 /*yield*/, server
219
+ .patch("/food/".concat(spinach._id))
220
+ .send({
221
+ name: "NotSpinach",
222
+ })
223
+ .expect(200)];
1246
224
  case 1:
1247
225
  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);
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();
1251
229
  return [2 /*return*/];
1252
230
  }
1253
231
  });
1254
232
  }); });
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)];
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)()];
1263
243
  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)];
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
+ ])];
1275
264
  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)];
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);
269
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
270
+ allowAnonymous: true,
271
+ permissions: {
272
+ create: [permissions_1.Permissions.IsAny],
273
+ delete: [permissions_1.Permissions.IsAny],
274
+ list: [permissions_1.Permissions.IsAny],
275
+ read: [permissions_1.Permissions.IsAny],
276
+ update: [permissions_1.Permissions.IsAny],
277
+ },
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
+ },
290
+ }));
291
+ server = (0, supertest_1.default)(app);
292
+ return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
1292
293
  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);
294
+ agent = _c.sent();
1312
295
  return [2 /*return*/];
1313
296
  }
1314
297
  });
1315
298
  }); });
1316
- (0, bun_test_1.it)("query with a space", function () { return __awaiter(void 0, void 0, void 0, function () {
1317
- var greenBeans, res;
299
+ (0, bun_test_1.it)("reads with serialize", function () { return __awaiter(void 0, void 0, void 0, function () {
300
+ var res;
1318
301
  return __generator(this, function (_a) {
1319
302
  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
- })];
303
+ case 0: return [4 /*yield*/, agent.get("/food/".concat(spinach._id)).expect(200)];
1326
304
  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
305
  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");
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");
1334
309
  return [2 /*return*/];
1335
310
  }
1336
311
  });
1337
312
  }); });
1338
- (0, bun_test_1.it)("query with a regex", function () { return __awaiter(void 0, void 0, void 0, function () {
1339
- var greenBeans, res;
313
+ (0, bun_test_1.it)("list with serialize", function () { return __awaiter(void 0, void 0, void 0, function () {
314
+ var res;
1340
315
  return __generator(this, function (_a) {
1341
316
  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
- })];
317
+ case 0: return [4 /*yield*/, agent.get("/food").expect(200)];
1348
318
  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
319
  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);
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");
1369
326
  return [2 /*return*/];
1370
327
  }
1371
328
  });
1372
329
  }); });
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;
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 () {
1375
334
  return __generator(this, function (_a) {
1376
335
  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)];
336
+ case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1384
337
  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)];
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, {
343
+ allowAnonymous: true,
344
+ permissions: {
345
+ create: [permissions_1.Permissions.IsAny],
346
+ delete: [permissions_1.Permissions.IsAny],
347
+ list: [permissions_1.Permissions.IsAny],
348
+ read: [permissions_1.Permissions.IsAny],
349
+ update: [permissions_1.Permissions.IsAny],
350
+ },
351
+ }));
352
+ server = (0, supertest_1.default)(app);
353
+ return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
1396
354
  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);
355
+ agent = _a.sent();
1402
356
  return [2 /*return*/];
1403
357
  }
1404
358
  });
1405
359
  }); });
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;
360
+ (0, bun_test_1.it)("check that security fields are filtered", function () { return __awaiter(void 0, void 0, void 0, function () {
361
+ var res;
1408
362
  return __generator(this, function (_a) {
1409
363
  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)];
364
+ case 0: return [4 /*yield*/, agent.get("/users").expect(200)];
1417
365
  case 1:
1418
366
  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);
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();
1425
371
  return [2 /*return*/];
1426
372
  }
1427
373
  });
1428
374
  }); });
1429
- (0, bun_test_1.it)("query $and operator on same field", function () { return __awaiter(void 0, void 0, void 0, function () {
375
+ });
376
+ (0, bun_test_1.describe)("error handling", function () {
377
+ var admin;
378
+ var spinach;
379
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
380
+ var _a;
381
+ return __generator(this, function (_b) {
382
+ switch (_b.label) {
383
+ case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
384
+ case 1:
385
+ _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
386
+ return [4 /*yield*/, tests_1.FoodModel.create({
387
+ calories: 1,
388
+ created: new Date("2021-12-03T00:00:20.000Z"),
389
+ hidden: false,
390
+ name: "Spinach",
391
+ ownerId: admin._id,
392
+ source: {
393
+ name: "Brand",
394
+ },
395
+ })];
396
+ case 2:
397
+ spinach = _b.sent();
398
+ app = (0, tests_1.getBaseServer)();
399
+ (0, auth_1.setupAuth)(app, tests_1.UserModel);
400
+ (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
401
+ return [2 /*return*/];
402
+ }
403
+ });
404
+ }); });
405
+ (0, bun_test_1.it)("PUT returns 500 not supported", function () { return __awaiter(void 0, void 0, void 0, function () {
1430
406
  var res;
1431
407
  return __generator(this, function (_a) {
1432
408
  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)];
409
+ case 0:
410
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
411
+ allowAnonymous: true,
412
+ permissions: {
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],
418
+ },
419
+ }));
420
+ server = (0, supertest_1.default)(app);
421
+ return [4 /*yield*/, server.put("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(500)];
1436
422
  case 1:
1437
423
  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());
424
+ (0, bun_test_1.expect)(res.body.title).toBe("PUT is not supported.");
1440
425
  return [2 /*return*/];
1441
426
  }
1442
427
  });
1443
428
  }); });
1444
- (0, bun_test_1.it)("query $and operator on same field, nested objects", 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 () {
1445
430
  var res;
1446
431
  return __generator(this, function (_a) {
1447
432
  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)];
433
+ case 0:
434
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
435
+ allowAnonymous: true,
436
+ permissions: {
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 {};
448
+ },
449
+ }));
450
+ server = (0, supertest_1.default)(app);
451
+ return [4 /*yield*/, server.get("/food/".concat(spinach._id)).expect(500)];
1453
452
  case 1:
1454
453
  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());
454
+ (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
1457
455
  return [2 /*return*/];
1458
456
  }
1459
457
  });
1460
458
  }); });
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;
459
+ (0, bun_test_1.it)("responseHandler error in create is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
460
+ var res;
1463
461
  return __generator(this, function (_a) {
1464
462
  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)];
463
+ case 0:
464
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
465
+ allowAnonymous: true,
466
+ permissions: {
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],
472
+ },
473
+ responseHandler: function (_data, method) {
474
+ if (method === "create") {
475
+ throw new Error("responseHandler create failed");
476
+ }
477
+ return {};
478
+ },
479
+ }));
480
+ server = (0, supertest_1.default)(app);
481
+ return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(500)];
1468
482
  case 1:
1469
483
  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);
484
+ (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
1474
485
  return [2 /*return*/];
1475
486
  }
1476
487
  });
1477
488
  }); });
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;
489
+ (0, bun_test_1.it)("responseHandler error in update is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
490
+ var res;
1480
491
  return __generator(this, function (_a) {
1481
492
  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)];
493
+ case 0:
494
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
495
+ allowAnonymous: true,
496
+ permissions: {
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],
502
+ },
503
+ responseHandler: function (_data, method) {
504
+ if (method === "update") {
505
+ throw new Error("responseHandler update failed");
506
+ }
507
+ return {};
508
+ },
509
+ }));
510
+ server = (0, supertest_1.default)(app);
511
+ return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(500)];
1488
512
  case 1:
1489
513
  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);
514
+ (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
1494
515
  return [2 /*return*/];
1495
516
  }
1496
517
  });
1497
518
  }); });
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 () {
519
+ (0, bun_test_1.it)("responseHandler error in list is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
1499
520
  var res;
1500
521
  return __generator(this, function (_a) {
1501
522
  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)];
523
+ case 0:
524
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
525
+ allowAnonymous: true,
526
+ permissions: {
527
+ create: [permissions_1.Permissions.IsAny],
528
+ delete: [permissions_1.Permissions.IsAny],
529
+ list: [permissions_1.Permissions.IsAny],
530
+ read: [permissions_1.Permissions.IsAny],
531
+ update: [permissions_1.Permissions.IsAny],
532
+ },
533
+ responseHandler: function (_data, method) {
534
+ if (method === "list") {
535
+ throw new Error("responseHandler list failed");
536
+ }
537
+ return {};
538
+ },
539
+ }));
540
+ server = (0, supertest_1.default)(app);
541
+ return [4 /*yield*/, server.get("/food").expect(500)];
1505
542
  case 1:
1506
543
  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.");
544
+ (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
1521
545
  return [2 /*return*/];
1522
546
  }
1523
547
  });
1524
548
  }); });
1525
- (0, bun_test_1.it)("query with a number", 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 () {
1526
550
  var res;
1527
551
  return __generator(this, function (_a) {
1528
552
  switch (_a.label) {
1529
- case 0: return [4 /*yield*/, agent.get("/food?calories=100").expect(200)];
553
+ case 0:
554
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
555
+ allowAnonymous: true,
556
+ permissions: {
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 {};
568
+ },
569
+ }));
570
+ server = (0, supertest_1.default)(app);
571
+ return [4 /*yield*/, server.get("/food").expect(200)];
1530
572
  case 1:
1531
573
  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());
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();
1534
577
  return [2 /*return*/];
1535
578
  }
1536
579
  });
1537
580
  }); });
1538
- (0, bun_test_1.it)("update", 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 () {
1539
582
  var res;
1540
583
  return __generator(this, function (_a) {
1541
584
  switch (_a.label) {
1542
- case 0: return [4 /*yield*/, agent.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(200)];
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
+ })];
1543
592
  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)];
593
+ _a.sent();
594
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
595
+ allowAnonymous: true,
596
+ permissions: {
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],
602
+ },
603
+ queryFields: ["name"],
604
+ }));
605
+ server = (0, supertest_1.default)(app);
606
+ return [4 /*yield*/, server.get("/food?sort=name").expect(200)];
1552
607
  case 2:
1553
- // Update a Map field.
1554
608
  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)];
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)];
1570
612
  case 3:
1571
- // Update a Map field.
1572
613
  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
- });
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");
1577
616
  return [2 /*return*/];
1578
617
  }
1579
618
  });
1580
619
  }); });
1581
- (0, bun_test_1.it)("update using dot notation", function () { return __awaiter(void 0, void 0, void 0, function () {
1582
- var res, dbSpinach;
620
+ (0, bun_test_1.it)("queryFilter error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
621
+ var res;
1583
622
  return __generator(this, function (_a) {
1584
623
  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);
624
+ case 0:
1646
625
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1647
626
  allowAnonymous: true,
1648
627
  permissions: {
@@ -1652,126 +631,160 @@ var tests_1 = require("./tests");
1652
631
  read: [permissions_1.Permissions.IsAny],
1653
632
  update: [permissions_1.Permissions.IsAny],
1654
633
  },
1655
- populatePaths: [{ fields: ["email"], path: "ownerId" }],
1656
- sort: "-created",
634
+ queryFilter: function () {
635
+ throw new Error("queryFilter failed");
636
+ },
1657
637
  }));
1658
638
  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)];
639
+ return [4 /*yield*/, server.get("/food").expect(400)];
1671
640
  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();
641
+ res = _a.sent();
642
+ (0, bun_test_1.expect)(res.body.title).toContain("Query filter error");
1681
643
  return [2 /*return*/];
1682
644
  }
1683
645
  });
1684
646
  }); });
1685
- (0, bun_test_1.it)("reads with populate", 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 () {
1686
648
  var res;
1687
649
  return __generator(this, function (_a) {
1688
650
  switch (_a.label) {
1689
- case 0: return [4 /*yield*/, agent.get("/food/".concat(spinach._id)).expect(200)];
651
+ case 0:
652
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
653
+ allowAnonymous: true,
654
+ endpoints: function (router) {
655
+ router.get("/custom", function (_req, res) {
656
+ res.json({ custom: true });
657
+ });
658
+ },
659
+ permissions: {
660
+ create: [permissions_1.Permissions.IsAny],
661
+ delete: [permissions_1.Permissions.IsAny],
662
+ list: [permissions_1.Permissions.IsAny],
663
+ read: [permissions_1.Permissions.IsAny],
664
+ update: [permissions_1.Permissions.IsAny],
665
+ },
666
+ }));
667
+ server = (0, supertest_1.default)(app);
668
+ return [4 /*yield*/, server.get("/food/custom").expect(200)];
1690
669
  case 1:
1691
670
  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();
671
+ (0, bun_test_1.expect)(res.body.custom).toBe(true);
1695
672
  return [2 /*return*/];
1696
673
  }
1697
674
  });
1698
675
  }); });
1699
- (0, bun_test_1.it)("creates with populate", function () { return __awaiter(void 0, void 0, void 0, function () {
676
+ (0, bun_test_1.it)("disallowed query param returns 400", function () { return __awaiter(void 0, void 0, void 0, function () {
1700
677
  var res;
1701
678
  return __generator(this, function (_a) {
1702
679
  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)];
680
+ case 0:
681
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
682
+ allowAnonymous: true,
683
+ permissions: {
684
+ create: [permissions_1.Permissions.IsAny],
685
+ delete: [permissions_1.Permissions.IsAny],
686
+ list: [permissions_1.Permissions.IsAny],
687
+ read: [permissions_1.Permissions.IsAny],
688
+ update: [permissions_1.Permissions.IsAny],
689
+ },
690
+ queryFields: ["name"],
691
+ }));
692
+ server = (0, supertest_1.default)(app);
693
+ return [4 /*yield*/, server.get("/food?calories=100").expect(400)];
1711
694
  case 1:
1712
695
  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();
696
+ (0, bun_test_1.expect)(res.body.title).toContain("calories is not allowed as a query param");
1716
697
  return [2 /*return*/];
1717
698
  }
1718
699
  });
1719
700
  }); });
1720
- (0, bun_test_1.it)("updates with populate", 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 () {
1721
702
  var res;
1722
703
  return __generator(this, function (_a) {
1723
704
  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)];
705
+ case 0:
706
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
707
+ allowAnonymous: true,
708
+ permissions: {
709
+ create: [permissions_1.Permissions.IsAny],
710
+ delete: [permissions_1.Permissions.IsAny],
711
+ list: [permissions_1.Permissions.IsAny],
712
+ read: [permissions_1.Permissions.IsAny],
713
+ update: [permissions_1.Permissions.IsAny],
714
+ },
715
+ queryFilter: function () { return null; },
716
+ }));
717
+ server = (0, supertest_1.default)(app);
718
+ return [4 /*yield*/, server.get("/food").expect(200)];
1730
719
  case 1:
1731
720
  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();
721
+ (0, bun_test_1.expect)(res.body.data).toEqual([]);
1735
722
  return [2 /*return*/];
1736
723
  }
1737
724
  });
1738
725
  }); });
1739
726
  });
1740
- (0, bun_test_1.describe)("responseHandler", function () {
727
+ (0, bun_test_1.describe)("transformer errors", function () {
1741
728
  var admin;
1742
- var agent;
1743
729
  var spinach;
1744
730
  (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) {
731
+ var _a;
732
+ return __generator(this, function (_b) {
733
+ switch (_b.label) {
1748
734
  case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1749
735
  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
- ])];
736
+ _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
737
+ return [4 /*yield*/, tests_1.FoodModel.create({
738
+ calories: 1,
739
+ created: new Date("2021-12-03T00:00:20.000Z"),
740
+ hidden: false,
741
+ name: "Spinach",
742
+ ownerId: admin._id,
743
+ source: {
744
+ name: "Brand",
745
+ },
746
+ })];
1770
747
  case 2:
1771
- _b = __read.apply(void 0, [_c.sent(), 1]), spinach = _b[0];
748
+ spinach = _b.sent();
1772
749
  app = (0, tests_1.getBaseServer)();
1773
750
  (0, auth_1.setupAuth)(app, tests_1.UserModel);
1774
751
  (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
752
+ return [2 /*return*/];
753
+ }
754
+ });
755
+ }); });
756
+ (0, bun_test_1.it)("transform error in create is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
757
+ var res;
758
+ return __generator(this, function (_a) {
759
+ switch (_a.label) {
760
+ case 0:
761
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
762
+ allowAnonymous: true,
763
+ permissions: {
764
+ create: [permissions_1.Permissions.IsAny],
765
+ delete: [permissions_1.Permissions.IsAny],
766
+ list: [permissions_1.Permissions.IsAny],
767
+ read: [permissions_1.Permissions.IsAny],
768
+ update: [permissions_1.Permissions.IsAny],
769
+ },
770
+ transformer: (0, transformers_1.AdminOwnerTransformer)({
771
+ anonWriteFields: ["name"],
772
+ }),
773
+ }));
774
+ server = (0, supertest_1.default)(app);
775
+ return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
776
+ case 1:
777
+ res = _a.sent();
778
+ (0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
779
+ return [2 /*return*/];
780
+ }
781
+ });
782
+ }); });
783
+ (0, bun_test_1.it)("transform error in patch is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
784
+ var res;
785
+ return __generator(this, function (_a) {
786
+ switch (_a.label) {
787
+ case 0:
1775
788
  app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1776
789
  allowAnonymous: true,
1777
790
  permissions: {
@@ -1781,360 +794,360 @@ var tests_1 = require("./tests");
1781
794
  read: [permissions_1.Permissions.IsAny],
1782
795
  update: [permissions_1.Permissions.IsAny],
1783
796
  },
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
- },
797
+ transformer: (0, transformers_1.AdminOwnerTransformer)({
798
+ anonWriteFields: ["name"],
799
+ }),
1796
800
  }));
1797
801
  server = (0, supertest_1.default)(app);
1798
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
1799
- case 3:
1800
- agent = _c.sent();
802
+ return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ calories: 100 }).expect(403)];
803
+ case 1:
804
+ res = _a.sent();
805
+ (0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
1801
806
  return [2 /*return*/];
1802
807
  }
1803
808
  });
1804
809
  }); });
1805
- (0, bun_test_1.it)("reads with serialize", 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 () {
1806
811
  var res;
1807
812
  return __generator(this, function (_a) {
1808
813
  switch (_a.label) {
1809
- case 0: return [4 /*yield*/, agent.get("/food/".concat(spinach._id)).expect(200)];
814
+ case 0:
815
+ app.use("/required", (0, api_1.modelRouter)(tests_1.RequiredModel, {
816
+ allowAnonymous: true,
817
+ permissions: {
818
+ create: [permissions_1.Permissions.IsAny],
819
+ delete: [permissions_1.Permissions.IsAny],
820
+ list: [permissions_1.Permissions.IsAny],
821
+ read: [permissions_1.Permissions.IsAny],
822
+ update: [permissions_1.Permissions.IsAny],
823
+ },
824
+ }));
825
+ server = (0, supertest_1.default)(app);
826
+ return [4 /*yield*/, server.post("/required").send({ about: "test" }).expect(400)];
1810
827
  case 1:
1811
828
  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");
829
+ (0, bun_test_1.expect)(res.body.title).toContain("Required");
1815
830
  return [2 /*return*/];
1816
831
  }
1817
832
  });
1818
833
  }); });
1819
- (0, bun_test_1.it)("list with serialize", function () { return __awaiter(void 0, void 0, void 0, function () {
1820
- var res;
834
+ });
835
+ (0, bun_test_1.describe)("addPopulateToQuery", function () {
836
+ (0, bun_test_1.it)("returns query unchanged with no populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
837
+ var query, result;
1821
838
  return __generator(this, function (_a) {
1822
839
  switch (_a.label) {
1823
- case 0: return [4 /*yield*/, agent.get("/food").expect(200)];
840
+ case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1824
841
  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");
842
+ _a.sent();
843
+ query = tests_1.FoodModel.find({});
844
+ result = (0, api_1.addPopulateToQuery)(query, undefined);
845
+ (0, bun_test_1.expect)(result).toBe(query);
1832
846
  return [2 /*return*/];
1833
847
  }
1834
848
  });
1835
849
  }); });
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 () {
850
+ (0, bun_test_1.it)("returns query unchanged with empty populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
851
+ var query, result;
1840
852
  return __generator(this, function (_a) {
1841
853
  switch (_a.label) {
1842
854
  case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1843
855
  case 1:
1844
856
  _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();
857
+ query = tests_1.FoodModel.find({});
858
+ result = (0, api_1.addPopulateToQuery)(query, []);
859
+ (0, bun_test_1.expect)(result).toBe(query);
1862
860
  return [2 /*return*/];
1863
861
  }
1864
862
  });
1865
863
  }); });
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;
864
+ (0, bun_test_1.it)("applies multiple populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
865
+ var query, result;
1868
866
  return __generator(this, function (_a) {
1869
867
  switch (_a.label) {
1870
- case 0: return [4 /*yield*/, agent.get("/users").expect(200)];
868
+ case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1871
869
  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();
870
+ _a.sent();
871
+ query = tests_1.FoodModel.find({});
872
+ result = (0, api_1.addPopulateToQuery)(query, [
873
+ { fields: ["email"], path: "ownerId" },
874
+ { fields: ["name"], path: "eatenBy" },
875
+ ]);
876
+ (0, bun_test_1.expect)(result).toBeDefined();
1877
877
  return [2 /*return*/];
1878
878
  }
1879
879
  });
1880
880
  }); });
1881
881
  });
1882
- (0, bun_test_1.describe)("discriminator", function () {
1883
- var superUser;
1884
- var staffUser;
1885
- var notAdmin;
882
+ (0, bun_test_1.describe)("soft delete with isDeleted plugin", function () {
883
+ var admin;
1886
884
  var agent;
1887
885
  (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
1888
- var _a, staffUserId, superUserId;
1889
- var _b;
1890
- return __generator(this, function (_c) {
1891
- switch (_c.label) {
886
+ var _a;
887
+ return __generator(this, function (_b) {
888
+ switch (_b.label) {
1892
889
  case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1893
890
  case 1:
1894
- _b = __read.apply(void 0, [_c.sent(), 1]), notAdmin = _b[0];
1895
- return [4 /*yield*/, Promise.all([
1896
- tests_1.StaffUserModel.create({
1897
- department: "Accounting",
1898
- email: "staff@example.com",
1899
- }),
1900
- tests_1.SuperUserModel.create({
1901
- email: "superuser@example.com",
1902
- superTitle: "Super Man",
1903
- }),
1904
- ])];
1905
- case 2:
1906
- _a = __read.apply(void 0, [_c.sent(), 2]), staffUserId = _a[0], superUserId = _a[1];
1907
- return [4 /*yield*/, tests_1.UserModel.findById(staffUserId)];
1908
- case 3:
1909
- staffUser = (_c.sent());
1910
- return [4 /*yield*/, tests_1.UserModel.findById(superUserId)];
1911
- case 4:
1912
- superUser = (_c.sent());
891
+ _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
1913
892
  app = (0, tests_1.getBaseServer)();
1914
893
  (0, auth_1.setupAuth)(app, tests_1.UserModel);
1915
894
  (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
895
+ return [2 /*return*/];
896
+ }
897
+ });
898
+ }); });
899
+ (0, bun_test_1.it)("soft deletes user with deleted field", function () { return __awaiter(void 0, void 0, void 0, function () {
900
+ var res, deletedUser;
901
+ return __generator(this, function (_a) {
902
+ switch (_a.label) {
903
+ case 0:
1916
904
  app.use("/users", (0, api_1.modelRouter)(tests_1.UserModel, {
1917
905
  allowAnonymous: true,
1918
- discriminatorKey: "__t",
1919
906
  permissions: {
1920
- create: [permissions_1.Permissions.IsAuthenticated],
1921
- delete: [permissions_1.Permissions.IsAuthenticated],
1922
- list: [permissions_1.Permissions.IsAuthenticated],
1923
- read: [permissions_1.Permissions.IsAuthenticated],
1924
- update: [permissions_1.Permissions.IsAuthenticated],
907
+ create: [permissions_1.Permissions.IsAny],
908
+ delete: [permissions_1.Permissions.IsAny],
909
+ list: [permissions_1.Permissions.IsAny],
910
+ read: [permissions_1.Permissions.IsAny],
911
+ update: [permissions_1.Permissions.IsAny],
1925
912
  },
1926
913
  }));
1927
914
  server = (0, supertest_1.default)(app);
1928
915
  return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
1929
- case 5:
1930
- agent = _c.sent();
916
+ case 1:
917
+ agent = _a.sent();
918
+ return [4 /*yield*/, agent.delete("/users/".concat(admin._id)).expect(204)];
919
+ case 2:
920
+ res = _a.sent();
921
+ (0, bun_test_1.expect)(res.body).toEqual({});
922
+ return [4 /*yield*/, tests_1.UserModel.findById(admin._id)];
923
+ case 3:
924
+ deletedUser = _a.sent();
925
+ (0, bun_test_1.expect)(deletedUser).toBeNull();
1931
926
  return [2 /*return*/];
1932
927
  }
1933
928
  });
1934
929
  }); });
1935
- (0, bun_test_1.it)("gets all users", function () { return __awaiter(void 0, void 0, void 0, function () {
1936
- var res, data;
1937
- return __generator(this, function (_a) {
1938
- switch (_a.label) {
1939
- case 0: return [4 /*yield*/, agent.get("/users").expect(200)];
930
+ });
931
+ (0, bun_test_1.describe)("populate in create", function () {
932
+ var admin;
933
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
934
+ var _a;
935
+ return __generator(this, function (_b) {
936
+ switch (_b.label) {
937
+ case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1940
938
  case 1:
1941
- res = _a.sent();
1942
- (0, bun_test_1.expect)(res.body.data).toHaveLength(5);
1943
- data = (0, sortBy_1.default)(res.body.data, ["email"]);
1944
- (0, bun_test_1.expect)(data[0].email).toBe("admin+other@example.com");
1945
- (0, bun_test_1.expect)(data[0].department).toBeUndefined();
1946
- (0, bun_test_1.expect)(data[0].supertitle).toBeUndefined();
1947
- (0, bun_test_1.expect)(data[0].__t).toBeUndefined();
1948
- (0, bun_test_1.expect)(data[1].email).toBe("admin@example.com");
1949
- (0, bun_test_1.expect)(data[1].department).toBeUndefined();
1950
- (0, bun_test_1.expect)(data[1].supertitle).toBeUndefined();
1951
- (0, bun_test_1.expect)(data[1].__t).toBeUndefined();
1952
- (0, bun_test_1.expect)(data[2].email).toBe("notAdmin@example.com");
1953
- (0, bun_test_1.expect)(data[2].department).toBeUndefined();
1954
- (0, bun_test_1.expect)(data[2].supertitle).toBeUndefined();
1955
- (0, bun_test_1.expect)(data[2].__t).toBeUndefined();
1956
- (0, bun_test_1.expect)(data[3].email).toBe("staff@example.com");
1957
- (0, bun_test_1.expect)(data[3].department).toBe("Accounting");
1958
- (0, bun_test_1.expect)(data[3].supertitle).toBeUndefined();
1959
- (0, bun_test_1.expect)(data[3].__t).toBe("Staff");
1960
- (0, bun_test_1.expect)(data[4].email).toBe("superuser@example.com");
1961
- (0, bun_test_1.expect)(data[4].department).toBeUndefined();
1962
- (0, bun_test_1.expect)(data[4].superTitle).toBe("Super Man");
1963
- (0, bun_test_1.expect)(data[4].__t).toBe("SuperUser");
939
+ _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
940
+ return [4 /*yield*/, tests_1.FoodModel.create({
941
+ calories: 1,
942
+ created: new Date("2021-12-03T00:00:20.000Z"),
943
+ hidden: false,
944
+ name: "Spinach",
945
+ ownerId: admin._id,
946
+ })];
947
+ case 2:
948
+ _b.sent();
949
+ app = (0, tests_1.getBaseServer)();
950
+ (0, auth_1.setupAuth)(app, tests_1.UserModel);
951
+ (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
1964
952
  return [2 /*return*/];
1965
953
  }
1966
954
  });
1967
955
  }); });
1968
- (0, bun_test_1.it)("gets a discriminated user", function () { return __awaiter(void 0, void 0, void 0, function () {
956
+ (0, bun_test_1.it)("handles populate with valid path in create", function () { return __awaiter(void 0, void 0, void 0, function () {
1969
957
  var res;
1970
958
  return __generator(this, function (_a) {
1971
959
  switch (_a.label) {
1972
- case 0: return [4 /*yield*/, agent.get("/users/".concat(superUser._id)).expect(200)];
960
+ case 0:
961
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
962
+ allowAnonymous: true,
963
+ permissions: {
964
+ create: [permissions_1.Permissions.IsAny],
965
+ delete: [permissions_1.Permissions.IsAny],
966
+ list: [permissions_1.Permissions.IsAny],
967
+ read: [permissions_1.Permissions.IsAny],
968
+ update: [permissions_1.Permissions.IsAny],
969
+ },
970
+ populatePaths: [{ fields: ["email"], path: "ownerId" }],
971
+ }));
972
+ server = (0, supertest_1.default)(app);
973
+ return [4 /*yield*/, server
974
+ .post("/food")
975
+ .send({ calories: 15, name: "Broccoli", ownerId: admin._id })
976
+ .expect(201)];
1973
977
  case 1:
1974
978
  res = _a.sent();
1975
- (0, bun_test_1.expect)(res.body.data.email).toBe("superuser@example.com");
1976
- (0, bun_test_1.expect)(res.body.data.department).toBeUndefined();
1977
- (0, bun_test_1.expect)(res.body.data.superTitle).toBe("Super Man");
979
+ (0, bun_test_1.expect)(res.body.data.name).toBe("Broccoli");
980
+ (0, bun_test_1.expect)(res.body.data.ownerId.email).toBe(admin.email);
1978
981
  return [2 /*return*/];
1979
982
  }
1980
983
  });
1981
984
  }); });
1982
- (0, bun_test_1.it)("updates a discriminated user", function () { return __awaiter(void 0, void 0, void 0, function () {
1983
- var res, user;
1984
- return __generator(this, function (_a) {
1985
- switch (_a.label) {
1986
- case 0:
1987
- // Fails without __t.
1988
- return [4 /*yield*/, agent.patch("/users/".concat(superUser._id)).send({ superTitle: "Batman" }).expect(404)];
985
+ });
986
+ (0, bun_test_1.describe)("save error handling", function () {
987
+ var admin;
988
+ var spinach;
989
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
990
+ var _a;
991
+ return __generator(this, function (_b) {
992
+ switch (_b.label) {
993
+ case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1989
994
  case 1:
1990
- // Fails without __t.
1991
- _a.sent();
1992
- return [4 /*yield*/, agent
1993
- .patch("/users/".concat(superUser._id))
1994
- .send({ __t: "SuperUser", superTitle: "Batman" })
1995
- .expect(200)];
995
+ _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
996
+ return [4 /*yield*/, tests_1.FoodModel.create({
997
+ calories: 1,
998
+ created: new Date("2021-12-03T00:00:20.000Z"),
999
+ hidden: false,
1000
+ name: "Spinach",
1001
+ ownerId: admin._id,
1002
+ source: {
1003
+ name: "Brand",
1004
+ },
1005
+ })];
1996
1006
  case 2:
1997
- res = _a.sent();
1998
- (0, bun_test_1.expect)(res.body.data.email).toBe("superuser@example.com");
1999
- (0, bun_test_1.expect)(res.body.data.department).toBeUndefined();
2000
- (0, bun_test_1.expect)(res.body.data.superTitle).toBe("Batman");
2001
- return [4 /*yield*/, tests_1.SuperUserModel.findById(superUser._id)];
2002
- case 3:
2003
- user = _a.sent();
2004
- (0, bun_test_1.expect)(user === null || user === void 0 ? void 0 : user.superTitle).toBe("Batman");
1007
+ spinach = _b.sent();
1008
+ app = (0, tests_1.getBaseServer)();
1009
+ (0, auth_1.setupAuth)(app, tests_1.UserModel);
1010
+ (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
2005
1011
  return [2 /*return*/];
2006
1012
  }
2007
1013
  });
2008
1014
  }); });
2009
- (0, bun_test_1.it)("updates a base user", function () { return __awaiter(void 0, void 0, void 0, function () {
2010
- var res, user;
1015
+ (0, bun_test_1.it)("handles patch save error with validation failure", function () { return __awaiter(void 0, void 0, void 0, function () {
1016
+ var res;
2011
1017
  return __generator(this, function (_a) {
2012
1018
  switch (_a.label) {
2013
- case 0: return [4 /*yield*/, agent
2014
- .patch("/users/".concat(notAdmin._id))
2015
- .send({ email: "newemail@example.com", superTitle: "The Boss" })
2016
- .expect(200)];
1019
+ case 0:
1020
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1021
+ allowAnonymous: true,
1022
+ permissions: {
1023
+ create: [permissions_1.Permissions.IsAny],
1024
+ delete: [permissions_1.Permissions.IsAny],
1025
+ list: [permissions_1.Permissions.IsAny],
1026
+ read: [permissions_1.Permissions.IsAny],
1027
+ update: [permissions_1.Permissions.IsAny],
1028
+ },
1029
+ }));
1030
+ server = (0, supertest_1.default)(app);
1031
+ return [4 /*yield*/, server
1032
+ .patch("/food/".concat(spinach._id))
1033
+ .send({ invalidField: "value" })
1034
+ .expect(400)];
2017
1035
  case 1:
2018
1036
  res = _a.sent();
2019
- (0, bun_test_1.expect)(res.body.data.email).toBe("newemail@example.com");
2020
- (0, bun_test_1.expect)(res.body.data.superTitle).toBeUndefined();
2021
- return [4 /*yield*/, tests_1.SuperUserModel.findById(notAdmin._id)];
2022
- case 2:
2023
- user = _a.sent();
2024
- (0, bun_test_1.expect)(user === null || user === void 0 ? void 0 : user.superTitle).toBeUndefined();
1037
+ (0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook save error");
2025
1038
  return [2 /*return*/];
2026
1039
  }
2027
1040
  });
2028
1041
  }); });
2029
- (0, bun_test_1.it)("cannot update discriminator key", function () { return __awaiter(void 0, void 0, void 0, function () {
1042
+ });
1043
+ (0, bun_test_1.describe)("body undefined after transform without preCreate", function () {
1044
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
2030
1045
  return __generator(this, function (_a) {
2031
1046
  switch (_a.label) {
2032
- case 0: return [4 /*yield*/, agent
2033
- .patch("/users/".concat(notAdmin._id))
2034
- .send({ __t: "Staff", superTitle: "Batman" })
2035
- .expect(404)];
1047
+ case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
2036
1048
  case 1:
2037
1049
  _a.sent();
2038
- return [4 /*yield*/, agent
2039
- .patch("/users/".concat(staffUser._id))
2040
- .send({ __t: "SuperUser", superTitle: "Batman" })
2041
- .expect(404)];
2042
- case 2:
2043
- _a.sent();
1050
+ app = (0, tests_1.getBaseServer)();
1051
+ (0, auth_1.setupAuth)(app, tests_1.UserModel);
1052
+ (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
2044
1053
  return [2 /*return*/];
2045
1054
  }
2046
1055
  });
2047
1056
  }); });
2048
- (0, bun_test_1.it)("updating a field on another discriminated model does nothing", function () { return __awaiter(void 0, void 0, void 0, function () {
2049
- var res, user;
1057
+ (0, bun_test_1.it)("handles undefined body after transform when no preCreate", function () { return __awaiter(void 0, void 0, void 0, function () {
1058
+ var res;
2050
1059
  return __generator(this, function (_a) {
2051
1060
  switch (_a.label) {
2052
- case 0: return [4 /*yield*/, agent
2053
- .patch("/users/".concat(superUser._id))
2054
- .send({ __t: "SuperUser", department: "Journalism" })
2055
- .expect(200)];
1061
+ case 0:
1062
+ app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1063
+ allowAnonymous: true,
1064
+ permissions: {
1065
+ create: [permissions_1.Permissions.IsAny],
1066
+ delete: [permissions_1.Permissions.IsAny],
1067
+ list: [permissions_1.Permissions.IsAny],
1068
+ read: [permissions_1.Permissions.IsAny],
1069
+ update: [permissions_1.Permissions.IsAny],
1070
+ },
1071
+ transformer: {
1072
+ transform: function () { return undefined; },
1073
+ },
1074
+ }));
1075
+ server = (0, supertest_1.default)(app);
1076
+ return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
2056
1077
  case 1:
2057
1078
  res = _a.sent();
2058
- (0, bun_test_1.expect)(res.body.data.department).toBeUndefined();
2059
- return [4 /*yield*/, tests_1.SuperUserModel.findById(superUser._id)];
2060
- case 2:
2061
- user = _a.sent();
2062
- (0, bun_test_1.expect)(user === null || user === void 0 ? void 0 : user.department).toBeUndefined();
1079
+ (0, bun_test_1.expect)(res.body.title).toBe("Invalid request body");
1080
+ (0, bun_test_1.expect)(res.body.detail).toBe("Body is undefined");
2063
1081
  return [2 /*return*/];
2064
1082
  }
2065
1083
  });
2066
1084
  }); });
2067
- (0, bun_test_1.it)("creates a discriminated user", function () { return __awaiter(void 0, void 0, void 0, function () {
2068
- var res, user;
2069
- return __generator(this, function (_a) {
2070
- switch (_a.label) {
2071
- case 0: return [4 /*yield*/, agent
2072
- .post("/users")
2073
- .send({
2074
- __t: "SuperUser",
2075
- department: "R&D",
2076
- email: "brucewayne@example.com",
2077
- superTitle: "Batman",
2078
- })
2079
- .expect(201)];
1085
+ });
1086
+ (0, bun_test_1.describe)("soft delete with deleted field", function () {
1087
+ var _admin;
1088
+ var agent;
1089
+ (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
1090
+ var _a;
1091
+ return __generator(this, function (_b) {
1092
+ switch (_b.label) {
1093
+ case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
2080
1094
  case 1:
2081
- res = _a.sent();
2082
- (0, bun_test_1.expect)(res.body.data.email).toBe("brucewayne@example.com");
2083
- // Because we pass __t, this should create a SuperUser which has no department, so this is
2084
- // dropped.
2085
- (0, bun_test_1.expect)(res.body.data.department).toBeUndefined();
2086
- (0, bun_test_1.expect)(res.body.data.superTitle).toBe("Batman");
2087
- return [4 /*yield*/, tests_1.SuperUserModel.findById(res.body.data._id)];
2088
- case 2:
2089
- user = _a.sent();
2090
- (0, bun_test_1.expect)(user === null || user === void 0 ? void 0 : user.superTitle).toBe("Batman");
1095
+ _a = __read.apply(void 0, [_b.sent(), 1]), _admin = _a[0];
1096
+ app = (0, tests_1.getBaseServer)();
1097
+ (0, auth_1.setupAuth)(app, tests_1.UserModel);
1098
+ (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
2091
1099
  return [2 /*return*/];
2092
1100
  }
2093
1101
  });
2094
1102
  }); });
2095
- (0, bun_test_1.it)("deletes a discriminated user", function () { return __awaiter(void 0, void 0, void 0, function () {
2096
- var user;
1103
+ (0, bun_test_1.it)("soft deletes document with deleted field using isDeletedPlugin", function () { return __awaiter(void 0, void 0, void 0, function () {
1104
+ var mongoose, softDeleteSchema, SoftDeleteModel, testDoc, softDeleted;
2097
1105
  return __generator(this, function (_a) {
2098
1106
  switch (_a.label) {
2099
- case 0:
2100
- // Fails without __t.
2101
- return [4 /*yield*/, agent.delete("/users/".concat(superUser._id)).expect(404)];
1107
+ case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("mongoose")); })];
2102
1108
  case 1:
2103
- // Fails without __t.
2104
- _a.sent();
2105
- return [4 /*yield*/, agent
2106
- .delete("/users/".concat(superUser._id))
2107
- .send({
2108
- __t: "SuperUser",
2109
- })
2110
- .expect(204)];
1109
+ mongoose = _a.sent();
1110
+ softDeleteSchema = new mongoose.Schema({
1111
+ deleted: { default: false, type: Boolean },
1112
+ name: String,
1113
+ });
1114
+ try {
1115
+ SoftDeleteModel = mongoose.model("SoftDeleteTest");
1116
+ }
1117
+ catch (_b) {
1118
+ SoftDeleteModel = mongoose.model("SoftDeleteTest", softDeleteSchema);
1119
+ }
1120
+ return [4 /*yield*/, SoftDeleteModel.deleteMany({})];
2111
1121
  case 2:
2112
1122
  _a.sent();
2113
- return [4 /*yield*/, tests_1.SuperUserModel.findById(superUser._id)];
1123
+ return [4 /*yield*/, SoftDeleteModel.create({ name: "TestItem" })];
2114
1124
  case 3:
2115
- user = _a.sent();
2116
- (0, bun_test_1.expect)(user).toBeNull();
2117
- return [2 /*return*/];
2118
- }
2119
- });
2120
- }); });
2121
- (0, bun_test_1.it)("deletes a base user", function () { return __awaiter(void 0, void 0, void 0, function () {
2122
- var user;
2123
- return __generator(this, function (_a) {
2124
- switch (_a.label) {
2125
- case 0:
2126
- // Fails for base user with __t
2127
- return [4 /*yield*/, agent.delete("/users/".concat(notAdmin._id)).send({ __t: "SuperUser" }).expect(404)];
2128
- case 1:
2129
- // Fails for base user with __t
1125
+ testDoc = _a.sent();
1126
+ app.use("/softdelete", (0, api_1.modelRouter)(SoftDeleteModel, {
1127
+ allowAnonymous: true,
1128
+ permissions: {
1129
+ create: [permissions_1.Permissions.IsAny],
1130
+ delete: [permissions_1.Permissions.IsAny],
1131
+ list: [permissions_1.Permissions.IsAny],
1132
+ read: [permissions_1.Permissions.IsAny],
1133
+ update: [permissions_1.Permissions.IsAny],
1134
+ },
1135
+ }));
1136
+ server = (0, supertest_1.default)(app);
1137
+ return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
1138
+ case 4:
1139
+ agent = _a.sent();
1140
+ return [4 /*yield*/, agent.delete("/softdelete/".concat(testDoc._id)).expect(204)];
1141
+ case 5:
2130
1142
  _a.sent();
2131
- return [4 /*yield*/, agent.delete("/users/".concat(notAdmin._id)).expect(204)];
2132
- case 2:
1143
+ return [4 /*yield*/, SoftDeleteModel.findById(testDoc._id)];
1144
+ case 6:
1145
+ softDeleted = _a.sent();
1146
+ (0, bun_test_1.expect)(softDeleted).not.toBeNull();
1147
+ (0, bun_test_1.expect)(softDeleted === null || softDeleted === void 0 ? void 0 : softDeleted.deleted).toBe(true);
1148
+ return [4 /*yield*/, SoftDeleteModel.deleteMany({})];
1149
+ case 7:
2133
1150
  _a.sent();
2134
- return [4 /*yield*/, tests_1.SuperUserModel.findById(notAdmin._id)];
2135
- case 3:
2136
- user = _a.sent();
2137
- (0, bun_test_1.expect)(user).toBeNull();
2138
1151
  return [2 /*return*/];
2139
1152
  }
2140
1153
  });