@terreno/api 0.0.14 → 0.0.16
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/biome.jsonc +0 -1
- package/dist/api.test.js +1514 -0
- package/package.json +1 -1
- package/src/api.test.ts +1247 -0
package/dist/api.test.js
CHANGED
|
@@ -92,6 +92,7 @@ var bun_test_1 = require("bun:test");
|
|
|
92
92
|
var supertest_1 = __importDefault(require("supertest"));
|
|
93
93
|
var api_1 = require("./api");
|
|
94
94
|
var auth_1 = require("./auth");
|
|
95
|
+
var errors_1 = require("./errors");
|
|
95
96
|
var permissions_1 = require("./permissions");
|
|
96
97
|
var tests_1 = require("./tests");
|
|
97
98
|
var transformers_1 = require("./transformers");
|
|
@@ -1152,5 +1153,1518 @@ var transformers_1 = require("./transformers");
|
|
|
1152
1153
|
}
|
|
1153
1154
|
});
|
|
1154
1155
|
}); });
|
|
1156
|
+
(0, bun_test_1.it)("array operation transform error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1157
|
+
var apple, res;
|
|
1158
|
+
return __generator(this, function (_a) {
|
|
1159
|
+
switch (_a.label) {
|
|
1160
|
+
case 0: return [4 /*yield*/, tests_1.FoodModel.create({
|
|
1161
|
+
calories: 95,
|
|
1162
|
+
created: new Date(),
|
|
1163
|
+
hidden: false,
|
|
1164
|
+
name: "Apple",
|
|
1165
|
+
tags: [],
|
|
1166
|
+
})];
|
|
1167
|
+
case 1:
|
|
1168
|
+
apple = _a.sent();
|
|
1169
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1170
|
+
allowAnonymous: true,
|
|
1171
|
+
permissions: {
|
|
1172
|
+
create: [permissions_1.Permissions.IsAdmin],
|
|
1173
|
+
delete: [permissions_1.Permissions.IsAdmin],
|
|
1174
|
+
list: [permissions_1.Permissions.IsAdmin],
|
|
1175
|
+
read: [permissions_1.Permissions.IsAdmin],
|
|
1176
|
+
update: [permissions_1.Permissions.IsAdmin],
|
|
1177
|
+
},
|
|
1178
|
+
transformer: (0, transformers_1.AdminOwnerTransformer)({
|
|
1179
|
+
adminWriteFields: ["name"],
|
|
1180
|
+
}),
|
|
1181
|
+
}));
|
|
1182
|
+
server = (0, supertest_1.default)(app);
|
|
1183
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
|
|
1184
|
+
case 2:
|
|
1185
|
+
agent = _a.sent();
|
|
1186
|
+
return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
|
|
1187
|
+
case 3:
|
|
1188
|
+
res = _a.sent();
|
|
1189
|
+
(0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
|
|
1190
|
+
return [2 /*return*/];
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
}); });
|
|
1194
|
+
});
|
|
1195
|
+
(0, bun_test_1.describe)("transformer errors", function () {
|
|
1196
|
+
var admin;
|
|
1197
|
+
var spinach;
|
|
1198
|
+
var agent;
|
|
1199
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1200
|
+
var _a;
|
|
1201
|
+
return __generator(this, function (_b) {
|
|
1202
|
+
switch (_b.label) {
|
|
1203
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1204
|
+
case 1:
|
|
1205
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
1206
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
1207
|
+
calories: 1,
|
|
1208
|
+
created: new Date("2021-12-03T00:00:20.000Z"),
|
|
1209
|
+
hidden: false,
|
|
1210
|
+
name: "Spinach",
|
|
1211
|
+
ownerId: admin._id,
|
|
1212
|
+
source: {
|
|
1213
|
+
name: "Brand",
|
|
1214
|
+
},
|
|
1215
|
+
})];
|
|
1216
|
+
case 2:
|
|
1217
|
+
spinach = _b.sent();
|
|
1218
|
+
app = (0, tests_1.getBaseServer)();
|
|
1219
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
1220
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
1221
|
+
return [2 /*return*/];
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1224
|
+
}); });
|
|
1225
|
+
(0, bun_test_1.it)("transform error in create is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1226
|
+
var res;
|
|
1227
|
+
return __generator(this, function (_a) {
|
|
1228
|
+
switch (_a.label) {
|
|
1229
|
+
case 0:
|
|
1230
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1231
|
+
allowAnonymous: true,
|
|
1232
|
+
permissions: {
|
|
1233
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1234
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1235
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1236
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1237
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1238
|
+
},
|
|
1239
|
+
transformer: (0, transformers_1.AdminOwnerTransformer)({
|
|
1240
|
+
// Only allow 'name' to be written, so 'calories' will throw
|
|
1241
|
+
anonWriteFields: ["name"],
|
|
1242
|
+
}),
|
|
1243
|
+
}));
|
|
1244
|
+
server = (0, supertest_1.default)(app);
|
|
1245
|
+
return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
|
|
1246
|
+
case 1:
|
|
1247
|
+
res = _a.sent();
|
|
1248
|
+
(0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
|
|
1249
|
+
return [2 /*return*/];
|
|
1250
|
+
}
|
|
1251
|
+
});
|
|
1252
|
+
}); });
|
|
1253
|
+
(0, bun_test_1.it)("transform error in patch is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1254
|
+
var res;
|
|
1255
|
+
return __generator(this, function (_a) {
|
|
1256
|
+
switch (_a.label) {
|
|
1257
|
+
case 0:
|
|
1258
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1259
|
+
allowAnonymous: true,
|
|
1260
|
+
permissions: {
|
|
1261
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1262
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1263
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1264
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1265
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1266
|
+
},
|
|
1267
|
+
transformer: (0, transformers_1.AdminOwnerTransformer)({
|
|
1268
|
+
// Only allow 'name' to be written, so 'calories' will throw
|
|
1269
|
+
anonWriteFields: ["name"],
|
|
1270
|
+
}),
|
|
1271
|
+
}));
|
|
1272
|
+
server = (0, supertest_1.default)(app);
|
|
1273
|
+
return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ calories: 100 }).expect(403)];
|
|
1274
|
+
case 1:
|
|
1275
|
+
res = _a.sent();
|
|
1276
|
+
(0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
|
|
1277
|
+
return [2 /*return*/];
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
}); });
|
|
1281
|
+
(0, bun_test_1.it)("model.create validation error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1282
|
+
var RequiredModel, res;
|
|
1283
|
+
return __generator(this, function (_a) {
|
|
1284
|
+
switch (_a.label) {
|
|
1285
|
+
case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./tests")); })];
|
|
1286
|
+
case 1:
|
|
1287
|
+
RequiredModel = (_a.sent()).RequiredModel;
|
|
1288
|
+
app.use("/required", (0, api_1.modelRouter)(RequiredModel, {
|
|
1289
|
+
allowAnonymous: true,
|
|
1290
|
+
permissions: {
|
|
1291
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1292
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1293
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1294
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1295
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1296
|
+
},
|
|
1297
|
+
}));
|
|
1298
|
+
server = (0, supertest_1.default)(app);
|
|
1299
|
+
return [4 /*yield*/, server.post("/required").send({ about: "test" }).expect(400)];
|
|
1300
|
+
case 2:
|
|
1301
|
+
res = _a.sent();
|
|
1302
|
+
(0, bun_test_1.expect)(res.body.title).toContain("Required");
|
|
1303
|
+
return [2 /*return*/];
|
|
1304
|
+
}
|
|
1305
|
+
});
|
|
1306
|
+
}); });
|
|
1307
|
+
(0, bun_test_1.it)("preDelete hook throwing APIError is re-thrown", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1308
|
+
var res;
|
|
1309
|
+
return __generator(this, function (_a) {
|
|
1310
|
+
switch (_a.label) {
|
|
1311
|
+
case 0:
|
|
1312
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1313
|
+
allowAnonymous: true,
|
|
1314
|
+
permissions: {
|
|
1315
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1316
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1317
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1318
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1319
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1320
|
+
},
|
|
1321
|
+
preDelete: function () {
|
|
1322
|
+
throw new errors_1.APIError({
|
|
1323
|
+
disableExternalErrorTracking: true,
|
|
1324
|
+
status: 400,
|
|
1325
|
+
title: "Custom preDelete APIError",
|
|
1326
|
+
});
|
|
1327
|
+
},
|
|
1328
|
+
}));
|
|
1329
|
+
server = (0, supertest_1.default)(app);
|
|
1330
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
1331
|
+
case 1:
|
|
1332
|
+
agent = _a.sent();
|
|
1333
|
+
return [4 /*yield*/, agent.delete("/food/".concat(spinach._id)).expect(400)];
|
|
1334
|
+
case 2:
|
|
1335
|
+
res = _a.sent();
|
|
1336
|
+
(0, bun_test_1.expect)(res.body.title).toBe("Custom preDelete APIError");
|
|
1337
|
+
(0, bun_test_1.expect)(res.body.disableExternalErrorTracking).toBe(true);
|
|
1338
|
+
return [2 /*return*/];
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
}); });
|
|
1342
|
+
});
|
|
1343
|
+
(0, bun_test_1.describe)("special query params", function () {
|
|
1344
|
+
var admin;
|
|
1345
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1346
|
+
var _a;
|
|
1347
|
+
return __generator(this, function (_b) {
|
|
1348
|
+
switch (_b.label) {
|
|
1349
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1350
|
+
case 1:
|
|
1351
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
1352
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
1353
|
+
calories: 1,
|
|
1354
|
+
created: new Date("2021-12-03T00:00:20.000Z"),
|
|
1355
|
+
hidden: false,
|
|
1356
|
+
name: "Spinach",
|
|
1357
|
+
ownerId: admin._id,
|
|
1358
|
+
})];
|
|
1359
|
+
case 2:
|
|
1360
|
+
_b.sent();
|
|
1361
|
+
app = (0, tests_1.getBaseServer)();
|
|
1362
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
1363
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
1364
|
+
return [2 /*return*/];
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
}); });
|
|
1368
|
+
(0, bun_test_1.it)("period query param is stripped from query", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1369
|
+
var res;
|
|
1370
|
+
return __generator(this, function (_a) {
|
|
1371
|
+
switch (_a.label) {
|
|
1372
|
+
case 0:
|
|
1373
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1374
|
+
allowAnonymous: true,
|
|
1375
|
+
permissions: {
|
|
1376
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1377
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1378
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1379
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1380
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1381
|
+
},
|
|
1382
|
+
queryFields: ["name", "period"],
|
|
1383
|
+
queryFilter: function (_user, query) {
|
|
1384
|
+
// Simulate a queryFilter that accepts and processes period
|
|
1385
|
+
if (query === null || query === void 0 ? void 0 : query.period) {
|
|
1386
|
+
// Period is processed but shouldn't be passed to mongo
|
|
1387
|
+
return query;
|
|
1388
|
+
}
|
|
1389
|
+
return query !== null && query !== void 0 ? query : {};
|
|
1390
|
+
},
|
|
1391
|
+
}));
|
|
1392
|
+
server = (0, supertest_1.default)(app);
|
|
1393
|
+
return [4 /*yield*/, server.get("/food?period=weekly").expect(200)];
|
|
1394
|
+
case 1:
|
|
1395
|
+
res = _a.sent();
|
|
1396
|
+
(0, bun_test_1.expect)(res.body.data).toBeDefined();
|
|
1397
|
+
return [2 /*return*/];
|
|
1398
|
+
}
|
|
1399
|
+
});
|
|
1400
|
+
}); });
|
|
1401
|
+
(0, bun_test_1.it)("query with false value", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1402
|
+
var res;
|
|
1403
|
+
return __generator(this, function (_a) {
|
|
1404
|
+
switch (_a.label) {
|
|
1405
|
+
case 0:
|
|
1406
|
+
// Create a food that is hidden
|
|
1407
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
1408
|
+
calories: 50,
|
|
1409
|
+
created: new Date("2021-12-04T00:00:20.000Z"),
|
|
1410
|
+
hidden: true,
|
|
1411
|
+
name: "HiddenFood",
|
|
1412
|
+
ownerId: admin._id,
|
|
1413
|
+
})];
|
|
1414
|
+
case 1:
|
|
1415
|
+
// Create a food that is hidden
|
|
1416
|
+
_a.sent();
|
|
1417
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1418
|
+
allowAnonymous: true,
|
|
1419
|
+
permissions: {
|
|
1420
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1421
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1422
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1423
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1424
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1425
|
+
},
|
|
1426
|
+
queryFields: ["name", "hidden"],
|
|
1427
|
+
}));
|
|
1428
|
+
server = (0, supertest_1.default)(app);
|
|
1429
|
+
return [4 /*yield*/, server.get("/food?hidden=false").expect(200)];
|
|
1430
|
+
case 2:
|
|
1431
|
+
res = _a.sent();
|
|
1432
|
+
(0, bun_test_1.expect)(res.body.data.every(function (f) { return f.hidden === false; })).toBe(true);
|
|
1433
|
+
return [2 /*return*/];
|
|
1434
|
+
}
|
|
1435
|
+
});
|
|
1436
|
+
}); });
|
|
1437
|
+
(0, bun_test_1.it)("$search query triggers special handling code path", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1438
|
+
var res;
|
|
1439
|
+
return __generator(this, function (_a) {
|
|
1440
|
+
switch (_a.label) {
|
|
1441
|
+
case 0:
|
|
1442
|
+
// The $search code path just accesses the collection but doesn't do anything with it
|
|
1443
|
+
// This test verifies the code path is exercised
|
|
1444
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1445
|
+
allowAnonymous: true,
|
|
1446
|
+
permissions: {
|
|
1447
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1448
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1449
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1450
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1451
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1452
|
+
},
|
|
1453
|
+
// Need to include $search in queryFields for it to pass validation
|
|
1454
|
+
queryFields: ["name", "$search"],
|
|
1455
|
+
}));
|
|
1456
|
+
server = (0, supertest_1.default)(app);
|
|
1457
|
+
return [4 /*yield*/, server.get("/food?$search=test")];
|
|
1458
|
+
case 1:
|
|
1459
|
+
res = _a.sent();
|
|
1460
|
+
// May return 500 because $search is passed to Mongo which doesn't support it without Atlas
|
|
1461
|
+
// The important thing is we've exercised the code path
|
|
1462
|
+
(0, bun_test_1.expect)(res.status === 200 || res.status === 500).toBe(true);
|
|
1463
|
+
return [2 /*return*/];
|
|
1464
|
+
}
|
|
1465
|
+
});
|
|
1466
|
+
}); });
|
|
1467
|
+
(0, bun_test_1.it)("$autocomplete query triggers special handling code path", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1468
|
+
var res;
|
|
1469
|
+
return __generator(this, function (_a) {
|
|
1470
|
+
switch (_a.label) {
|
|
1471
|
+
case 0:
|
|
1472
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1473
|
+
allowAnonymous: true,
|
|
1474
|
+
permissions: {
|
|
1475
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1476
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1477
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1478
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1479
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1480
|
+
},
|
|
1481
|
+
queryFields: ["name", "$autocomplete"],
|
|
1482
|
+
}));
|
|
1483
|
+
server = (0, supertest_1.default)(app);
|
|
1484
|
+
return [4 /*yield*/, server.get("/food?$autocomplete=test")];
|
|
1485
|
+
case 1:
|
|
1486
|
+
res = _a.sent();
|
|
1487
|
+
(0, bun_test_1.expect)(res.status === 200 || res.status === 500).toBe(true);
|
|
1488
|
+
return [2 /*return*/];
|
|
1489
|
+
}
|
|
1490
|
+
});
|
|
1491
|
+
}); });
|
|
1492
|
+
});
|
|
1493
|
+
(0, bun_test_1.describe)("addPopulateToQuery", function () {
|
|
1494
|
+
(0, bun_test_1.it)("returns query unchanged with no populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1495
|
+
var query, result;
|
|
1496
|
+
return __generator(this, function (_a) {
|
|
1497
|
+
switch (_a.label) {
|
|
1498
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1499
|
+
case 1:
|
|
1500
|
+
_a.sent();
|
|
1501
|
+
query = tests_1.FoodModel.find({});
|
|
1502
|
+
result = (0, api_1.addPopulateToQuery)(query, undefined);
|
|
1503
|
+
(0, bun_test_1.expect)(result).toBe(query);
|
|
1504
|
+
return [2 /*return*/];
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
}); });
|
|
1508
|
+
(0, bun_test_1.it)("returns query unchanged with empty populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1509
|
+
var query, result;
|
|
1510
|
+
return __generator(this, function (_a) {
|
|
1511
|
+
switch (_a.label) {
|
|
1512
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1513
|
+
case 1:
|
|
1514
|
+
_a.sent();
|
|
1515
|
+
query = tests_1.FoodModel.find({});
|
|
1516
|
+
result = (0, api_1.addPopulateToQuery)(query, []);
|
|
1517
|
+
(0, bun_test_1.expect)(result).toBe(query);
|
|
1518
|
+
return [2 /*return*/];
|
|
1519
|
+
}
|
|
1520
|
+
});
|
|
1521
|
+
}); });
|
|
1522
|
+
(0, bun_test_1.it)("applies multiple populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1523
|
+
var query, result;
|
|
1524
|
+
return __generator(this, function (_a) {
|
|
1525
|
+
switch (_a.label) {
|
|
1526
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1527
|
+
case 1:
|
|
1528
|
+
_a.sent();
|
|
1529
|
+
query = tests_1.FoodModel.find({});
|
|
1530
|
+
result = (0, api_1.addPopulateToQuery)(query, [
|
|
1531
|
+
{ fields: ["email"], path: "ownerId" },
|
|
1532
|
+
{ fields: ["name"], path: "eatenBy" },
|
|
1533
|
+
]);
|
|
1534
|
+
// The result should be a query with populate applied
|
|
1535
|
+
(0, bun_test_1.expect)(result).toBeDefined();
|
|
1536
|
+
return [2 /*return*/];
|
|
1537
|
+
}
|
|
1538
|
+
});
|
|
1539
|
+
}); });
|
|
1540
|
+
});
|
|
1541
|
+
(0, bun_test_1.describe)("soft delete with isDeleted plugin", function () {
|
|
1542
|
+
var admin;
|
|
1543
|
+
var agent;
|
|
1544
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1545
|
+
var _a;
|
|
1546
|
+
return __generator(this, function (_b) {
|
|
1547
|
+
switch (_b.label) {
|
|
1548
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1549
|
+
case 1:
|
|
1550
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
1551
|
+
app = (0, tests_1.getBaseServer)();
|
|
1552
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
1553
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
1554
|
+
return [2 /*return*/];
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
}); });
|
|
1558
|
+
(0, bun_test_1.it)("soft deletes user with deleted field", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1559
|
+
var res, deletedUser;
|
|
1560
|
+
return __generator(this, function (_a) {
|
|
1561
|
+
switch (_a.label) {
|
|
1562
|
+
case 0:
|
|
1563
|
+
// UserModel has the isDisabledPlugin which adds a 'disabled' field,
|
|
1564
|
+
// but we need to test the 'deleted' field check.
|
|
1565
|
+
// Let's use a model that has the deleted field.
|
|
1566
|
+
app.use("/users", (0, api_1.modelRouter)(tests_1.UserModel, {
|
|
1567
|
+
allowAnonymous: true,
|
|
1568
|
+
permissions: {
|
|
1569
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1570
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1571
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1572
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1573
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1574
|
+
},
|
|
1575
|
+
}));
|
|
1576
|
+
server = (0, supertest_1.default)(app);
|
|
1577
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
1578
|
+
case 1:
|
|
1579
|
+
agent = _a.sent();
|
|
1580
|
+
return [4 /*yield*/, agent.delete("/users/".concat(admin._id)).expect(204)];
|
|
1581
|
+
case 2:
|
|
1582
|
+
res = _a.sent();
|
|
1583
|
+
(0, bun_test_1.expect)(res.body).toEqual({});
|
|
1584
|
+
return [4 /*yield*/, tests_1.UserModel.findById(admin._id)];
|
|
1585
|
+
case 3:
|
|
1586
|
+
deletedUser = _a.sent();
|
|
1587
|
+
(0, bun_test_1.expect)(deletedUser).toBeNull();
|
|
1588
|
+
return [2 /*return*/];
|
|
1589
|
+
}
|
|
1590
|
+
});
|
|
1591
|
+
}); });
|
|
1592
|
+
});
|
|
1593
|
+
(0, bun_test_1.describe)("populate in create", function () {
|
|
1594
|
+
var admin;
|
|
1595
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1596
|
+
var _a;
|
|
1597
|
+
return __generator(this, function (_b) {
|
|
1598
|
+
switch (_b.label) {
|
|
1599
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1600
|
+
case 1:
|
|
1601
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
1602
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
1603
|
+
calories: 1,
|
|
1604
|
+
created: new Date("2021-12-03T00:00:20.000Z"),
|
|
1605
|
+
hidden: false,
|
|
1606
|
+
name: "Spinach",
|
|
1607
|
+
ownerId: admin._id,
|
|
1608
|
+
})];
|
|
1609
|
+
case 2:
|
|
1610
|
+
_b.sent();
|
|
1611
|
+
app = (0, tests_1.getBaseServer)();
|
|
1612
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
1613
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
1614
|
+
return [2 /*return*/];
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
}); });
|
|
1618
|
+
(0, bun_test_1.it)("handles populate with valid path in create", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1619
|
+
var res;
|
|
1620
|
+
return __generator(this, function (_a) {
|
|
1621
|
+
switch (_a.label) {
|
|
1622
|
+
case 0:
|
|
1623
|
+
// Test that valid populate works in create flow
|
|
1624
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1625
|
+
allowAnonymous: true,
|
|
1626
|
+
permissions: {
|
|
1627
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1628
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1629
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1630
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1631
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1632
|
+
},
|
|
1633
|
+
populatePaths: [{ fields: ["email"], path: "ownerId" }],
|
|
1634
|
+
}));
|
|
1635
|
+
server = (0, supertest_1.default)(app);
|
|
1636
|
+
return [4 /*yield*/, server
|
|
1637
|
+
.post("/food")
|
|
1638
|
+
.send({ calories: 15, name: "Broccoli", ownerId: admin._id })
|
|
1639
|
+
.expect(201)];
|
|
1640
|
+
case 1:
|
|
1641
|
+
res = _a.sent();
|
|
1642
|
+
(0, bun_test_1.expect)(res.body.data.name).toBe("Broccoli");
|
|
1643
|
+
// Verify populate worked - ownerId should be an object with email
|
|
1644
|
+
(0, bun_test_1.expect)(res.body.data.ownerId.email).toBe(admin.email);
|
|
1645
|
+
return [2 /*return*/];
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
}); });
|
|
1649
|
+
});
|
|
1650
|
+
(0, bun_test_1.describe)("save error handling", function () {
|
|
1651
|
+
var admin;
|
|
1652
|
+
var spinach;
|
|
1653
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1654
|
+
var _a;
|
|
1655
|
+
return __generator(this, function (_b) {
|
|
1656
|
+
switch (_b.label) {
|
|
1657
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1658
|
+
case 1:
|
|
1659
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
1660
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
1661
|
+
calories: 1,
|
|
1662
|
+
created: new Date("2021-12-03T00:00:20.000Z"),
|
|
1663
|
+
hidden: false,
|
|
1664
|
+
name: "Spinach",
|
|
1665
|
+
ownerId: admin._id,
|
|
1666
|
+
source: {
|
|
1667
|
+
name: "Brand",
|
|
1668
|
+
},
|
|
1669
|
+
})];
|
|
1670
|
+
case 2:
|
|
1671
|
+
spinach = _b.sent();
|
|
1672
|
+
app = (0, tests_1.getBaseServer)();
|
|
1673
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
1674
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
1675
|
+
return [2 /*return*/];
|
|
1676
|
+
}
|
|
1677
|
+
});
|
|
1678
|
+
}); });
|
|
1679
|
+
(0, bun_test_1.it)("handles patch save error with validation failure", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1680
|
+
var res;
|
|
1681
|
+
return __generator(this, function (_a) {
|
|
1682
|
+
switch (_a.label) {
|
|
1683
|
+
case 0:
|
|
1684
|
+
// The FoodModel has strict: "throw" which will cause validation errors for unknown fields
|
|
1685
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1686
|
+
allowAnonymous: true,
|
|
1687
|
+
permissions: {
|
|
1688
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1689
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1690
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1691
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1692
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1693
|
+
},
|
|
1694
|
+
}));
|
|
1695
|
+
server = (0, supertest_1.default)(app);
|
|
1696
|
+
return [4 /*yield*/, server
|
|
1697
|
+
.patch("/food/".concat(spinach._id))
|
|
1698
|
+
.send({ invalidField: "value" })
|
|
1699
|
+
.expect(400)];
|
|
1700
|
+
case 1:
|
|
1701
|
+
res = _a.sent();
|
|
1702
|
+
(0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook save error");
|
|
1703
|
+
return [2 /*return*/];
|
|
1704
|
+
}
|
|
1705
|
+
});
|
|
1706
|
+
}); });
|
|
1707
|
+
});
|
|
1708
|
+
(0, bun_test_1.describe)("body undefined after transform without preCreate", function () {
|
|
1709
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1710
|
+
return __generator(this, function (_a) {
|
|
1711
|
+
switch (_a.label) {
|
|
1712
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1713
|
+
case 1:
|
|
1714
|
+
_a.sent();
|
|
1715
|
+
app = (0, tests_1.getBaseServer)();
|
|
1716
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
1717
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
1718
|
+
return [2 /*return*/];
|
|
1719
|
+
}
|
|
1720
|
+
});
|
|
1721
|
+
}); });
|
|
1722
|
+
(0, bun_test_1.it)("handles undefined body after transform when no preCreate", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1723
|
+
var res;
|
|
1724
|
+
return __generator(this, function (_a) {
|
|
1725
|
+
switch (_a.label) {
|
|
1726
|
+
case 0:
|
|
1727
|
+
// Create a transformer that returns undefined
|
|
1728
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1729
|
+
allowAnonymous: true,
|
|
1730
|
+
permissions: {
|
|
1731
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1732
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1733
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1734
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1735
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1736
|
+
},
|
|
1737
|
+
transformer: {
|
|
1738
|
+
transform: function () { return undefined; },
|
|
1739
|
+
},
|
|
1740
|
+
}));
|
|
1741
|
+
server = (0, supertest_1.default)(app);
|
|
1742
|
+
return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
|
|
1743
|
+
case 1:
|
|
1744
|
+
res = _a.sent();
|
|
1745
|
+
(0, bun_test_1.expect)(res.body.title).toBe("Invalid request body");
|
|
1746
|
+
(0, bun_test_1.expect)(res.body.detail).toBe("Body is undefined");
|
|
1747
|
+
return [2 /*return*/];
|
|
1748
|
+
}
|
|
1749
|
+
});
|
|
1750
|
+
}); });
|
|
1751
|
+
});
|
|
1752
|
+
(0, bun_test_1.describe)("soft delete with deleted field", function () {
|
|
1753
|
+
var agent;
|
|
1754
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1755
|
+
return __generator(this, function (_a) {
|
|
1756
|
+
switch (_a.label) {
|
|
1757
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1758
|
+
case 1:
|
|
1759
|
+
_a.sent();
|
|
1760
|
+
app = (0, tests_1.getBaseServer)();
|
|
1761
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
1762
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
1763
|
+
return [2 /*return*/];
|
|
1764
|
+
}
|
|
1765
|
+
});
|
|
1766
|
+
}); });
|
|
1767
|
+
(0, bun_test_1.it)("soft deletes document with deleted field using isDeletedPlugin", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1768
|
+
var mongoose, softDeleteSchema, SoftDeleteModel, testDoc, softDeleted;
|
|
1769
|
+
return __generator(this, function (_a) {
|
|
1770
|
+
switch (_a.label) {
|
|
1771
|
+
case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("mongoose")); })];
|
|
1772
|
+
case 1:
|
|
1773
|
+
mongoose = _a.sent();
|
|
1774
|
+
softDeleteSchema = new mongoose.Schema({
|
|
1775
|
+
deleted: { default: false, type: Boolean },
|
|
1776
|
+
name: String,
|
|
1777
|
+
});
|
|
1778
|
+
try {
|
|
1779
|
+
SoftDeleteModel = mongoose.model("SoftDeleteTest");
|
|
1780
|
+
}
|
|
1781
|
+
catch (_b) {
|
|
1782
|
+
SoftDeleteModel = mongoose.model("SoftDeleteTest", softDeleteSchema);
|
|
1783
|
+
}
|
|
1784
|
+
// Clean up any existing documents
|
|
1785
|
+
return [4 /*yield*/, SoftDeleteModel.deleteMany({})];
|
|
1786
|
+
case 2:
|
|
1787
|
+
// Clean up any existing documents
|
|
1788
|
+
_a.sent();
|
|
1789
|
+
return [4 /*yield*/, SoftDeleteModel.create({ name: "TestItem" })];
|
|
1790
|
+
case 3:
|
|
1791
|
+
testDoc = _a.sent();
|
|
1792
|
+
app.use("/softdelete", (0, api_1.modelRouter)(SoftDeleteModel, {
|
|
1793
|
+
allowAnonymous: true,
|
|
1794
|
+
permissions: {
|
|
1795
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1796
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1797
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1798
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1799
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1800
|
+
},
|
|
1801
|
+
}));
|
|
1802
|
+
server = (0, supertest_1.default)(app);
|
|
1803
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
1804
|
+
case 4:
|
|
1805
|
+
agent = _a.sent();
|
|
1806
|
+
// Delete should soft delete (set deleted: true) instead of hard delete
|
|
1807
|
+
return [4 /*yield*/, agent.delete("/softdelete/".concat(testDoc._id)).expect(204)];
|
|
1808
|
+
case 5:
|
|
1809
|
+
// Delete should soft delete (set deleted: true) instead of hard delete
|
|
1810
|
+
_a.sent();
|
|
1811
|
+
return [4 /*yield*/, SoftDeleteModel.findById(testDoc._id)];
|
|
1812
|
+
case 6:
|
|
1813
|
+
softDeleted = _a.sent();
|
|
1814
|
+
(0, bun_test_1.expect)(softDeleted).not.toBeNull();
|
|
1815
|
+
(0, bun_test_1.expect)(softDeleted === null || softDeleted === void 0 ? void 0 : softDeleted.deleted).toBe(true);
|
|
1816
|
+
// Clean up
|
|
1817
|
+
return [4 /*yield*/, SoftDeleteModel.deleteMany({})];
|
|
1818
|
+
case 7:
|
|
1819
|
+
// Clean up
|
|
1820
|
+
_a.sent();
|
|
1821
|
+
return [2 /*return*/];
|
|
1822
|
+
}
|
|
1823
|
+
});
|
|
1824
|
+
}); });
|
|
1825
|
+
});
|
|
1826
|
+
(0, bun_test_1.describe)("array operation with undefined preUpdate return", function () {
|
|
1827
|
+
var admin;
|
|
1828
|
+
var apple;
|
|
1829
|
+
var agent;
|
|
1830
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1831
|
+
var _a;
|
|
1832
|
+
return __generator(this, function (_b) {
|
|
1833
|
+
switch (_b.label) {
|
|
1834
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1835
|
+
case 1:
|
|
1836
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
1837
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
1838
|
+
calories: 100,
|
|
1839
|
+
categories: [
|
|
1840
|
+
{ name: "Fruit", show: true },
|
|
1841
|
+
{ name: "Popular", show: false },
|
|
1842
|
+
],
|
|
1843
|
+
created: new Date("2021-12-03T00:00:30.000Z"),
|
|
1844
|
+
hidden: false,
|
|
1845
|
+
name: "Apple",
|
|
1846
|
+
ownerId: admin._id,
|
|
1847
|
+
tags: ["healthy", "cheap"],
|
|
1848
|
+
})];
|
|
1849
|
+
case 2:
|
|
1850
|
+
apple = _b.sent();
|
|
1851
|
+
app = (0, tests_1.getBaseServer)();
|
|
1852
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
1853
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
1854
|
+
return [2 /*return*/];
|
|
1855
|
+
}
|
|
1856
|
+
});
|
|
1857
|
+
}); });
|
|
1858
|
+
(0, bun_test_1.it)("array operation preUpdate returning undefined for array POST throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1859
|
+
var res;
|
|
1860
|
+
return __generator(this, function (_a) {
|
|
1861
|
+
switch (_a.label) {
|
|
1862
|
+
case 0:
|
|
1863
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1864
|
+
allowAnonymous: true,
|
|
1865
|
+
permissions: {
|
|
1866
|
+
create: [permissions_1.Permissions.IsAdmin],
|
|
1867
|
+
delete: [permissions_1.Permissions.IsAdmin],
|
|
1868
|
+
list: [permissions_1.Permissions.IsAdmin],
|
|
1869
|
+
read: [permissions_1.Permissions.IsAdmin],
|
|
1870
|
+
update: [permissions_1.Permissions.IsAdmin],
|
|
1871
|
+
},
|
|
1872
|
+
preUpdate: function () { return undefined; },
|
|
1873
|
+
}));
|
|
1874
|
+
server = (0, supertest_1.default)(app);
|
|
1875
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
|
|
1876
|
+
case 1:
|
|
1877
|
+
agent = _a.sent();
|
|
1878
|
+
return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
|
|
1879
|
+
case 2:
|
|
1880
|
+
res = _a.sent();
|
|
1881
|
+
(0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
|
|
1882
|
+
(0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preUpdate");
|
|
1883
|
+
return [2 /*return*/];
|
|
1884
|
+
}
|
|
1885
|
+
});
|
|
1886
|
+
}); });
|
|
1887
|
+
(0, bun_test_1.it)("array operation preUpdate returning null for array PATCH throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1888
|
+
var res;
|
|
1889
|
+
return __generator(this, function (_a) {
|
|
1890
|
+
switch (_a.label) {
|
|
1891
|
+
case 0:
|
|
1892
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1893
|
+
allowAnonymous: true,
|
|
1894
|
+
permissions: {
|
|
1895
|
+
create: [permissions_1.Permissions.IsAdmin],
|
|
1896
|
+
delete: [permissions_1.Permissions.IsAdmin],
|
|
1897
|
+
list: [permissions_1.Permissions.IsAdmin],
|
|
1898
|
+
read: [permissions_1.Permissions.IsAdmin],
|
|
1899
|
+
update: [permissions_1.Permissions.IsAdmin],
|
|
1900
|
+
},
|
|
1901
|
+
preUpdate: function () { return null; },
|
|
1902
|
+
}));
|
|
1903
|
+
server = (0, supertest_1.default)(app);
|
|
1904
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
|
|
1905
|
+
case 1:
|
|
1906
|
+
agent = _a.sent();
|
|
1907
|
+
return [4 /*yield*/, agent
|
|
1908
|
+
.patch("/food/".concat(apple._id, "/tags/healthy"))
|
|
1909
|
+
.send({ tags: "unhealthy" })
|
|
1910
|
+
.expect(403)];
|
|
1911
|
+
case 2:
|
|
1912
|
+
res = _a.sent();
|
|
1913
|
+
(0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
|
|
1914
|
+
return [2 /*return*/];
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
}); });
|
|
1918
|
+
(0, bun_test_1.it)("array operation preUpdate error for array DELETE is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1919
|
+
var res;
|
|
1920
|
+
return __generator(this, function (_a) {
|
|
1921
|
+
switch (_a.label) {
|
|
1922
|
+
case 0:
|
|
1923
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1924
|
+
allowAnonymous: true,
|
|
1925
|
+
permissions: {
|
|
1926
|
+
create: [permissions_1.Permissions.IsAdmin],
|
|
1927
|
+
delete: [permissions_1.Permissions.IsAdmin],
|
|
1928
|
+
list: [permissions_1.Permissions.IsAdmin],
|
|
1929
|
+
read: [permissions_1.Permissions.IsAdmin],
|
|
1930
|
+
update: [permissions_1.Permissions.IsAdmin],
|
|
1931
|
+
},
|
|
1932
|
+
preUpdate: function () {
|
|
1933
|
+
throw new Error("preUpdate error during delete");
|
|
1934
|
+
},
|
|
1935
|
+
}));
|
|
1936
|
+
server = (0, supertest_1.default)(app);
|
|
1937
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
|
|
1938
|
+
case 1:
|
|
1939
|
+
agent = _a.sent();
|
|
1940
|
+
return [4 /*yield*/, agent.delete("/food/".concat(apple._id, "/tags/healthy")).expect(400)];
|
|
1941
|
+
case 2:
|
|
1942
|
+
res = _a.sent();
|
|
1943
|
+
(0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook error");
|
|
1944
|
+
return [2 /*return*/];
|
|
1945
|
+
}
|
|
1946
|
+
});
|
|
1947
|
+
}); });
|
|
1948
|
+
});
|
|
1949
|
+
(0, bun_test_1.describe)("transformer errors", function () {
|
|
1950
|
+
var admin;
|
|
1951
|
+
var spinach;
|
|
1952
|
+
var _agent;
|
|
1953
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1954
|
+
var _a;
|
|
1955
|
+
return __generator(this, function (_b) {
|
|
1956
|
+
switch (_b.label) {
|
|
1957
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
1958
|
+
case 1:
|
|
1959
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
1960
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
1961
|
+
calories: 1,
|
|
1962
|
+
created: new Date("2021-12-03T00:00:20.000Z"),
|
|
1963
|
+
hidden: false,
|
|
1964
|
+
name: "Spinach",
|
|
1965
|
+
ownerId: admin._id,
|
|
1966
|
+
source: {
|
|
1967
|
+
name: "Brand",
|
|
1968
|
+
},
|
|
1969
|
+
})];
|
|
1970
|
+
case 2:
|
|
1971
|
+
spinach = _b.sent();
|
|
1972
|
+
app = (0, tests_1.getBaseServer)();
|
|
1973
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
1974
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
1975
|
+
return [2 /*return*/];
|
|
1976
|
+
}
|
|
1977
|
+
});
|
|
1978
|
+
}); });
|
|
1979
|
+
(0, bun_test_1.it)("transform error in create is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1980
|
+
var res;
|
|
1981
|
+
return __generator(this, function (_a) {
|
|
1982
|
+
switch (_a.label) {
|
|
1983
|
+
case 0:
|
|
1984
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
1985
|
+
allowAnonymous: true,
|
|
1986
|
+
permissions: {
|
|
1987
|
+
create: [permissions_1.Permissions.IsAny],
|
|
1988
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
1989
|
+
list: [permissions_1.Permissions.IsAny],
|
|
1990
|
+
read: [permissions_1.Permissions.IsAny],
|
|
1991
|
+
update: [permissions_1.Permissions.IsAny],
|
|
1992
|
+
},
|
|
1993
|
+
transformer: (0, transformers_1.AdminOwnerTransformer)({
|
|
1994
|
+
// Only allow 'name' to be written, so 'calories' will throw
|
|
1995
|
+
anonWriteFields: ["name"],
|
|
1996
|
+
}),
|
|
1997
|
+
}));
|
|
1998
|
+
server = (0, supertest_1.default)(app);
|
|
1999
|
+
return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
|
|
2000
|
+
case 1:
|
|
2001
|
+
res = _a.sent();
|
|
2002
|
+
(0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
|
|
2003
|
+
return [2 /*return*/];
|
|
2004
|
+
}
|
|
2005
|
+
});
|
|
2006
|
+
}); });
|
|
2007
|
+
(0, bun_test_1.it)("transform error in patch is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2008
|
+
var res;
|
|
2009
|
+
return __generator(this, function (_a) {
|
|
2010
|
+
switch (_a.label) {
|
|
2011
|
+
case 0:
|
|
2012
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
2013
|
+
allowAnonymous: true,
|
|
2014
|
+
permissions: {
|
|
2015
|
+
create: [permissions_1.Permissions.IsAny],
|
|
2016
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
2017
|
+
list: [permissions_1.Permissions.IsAny],
|
|
2018
|
+
read: [permissions_1.Permissions.IsAny],
|
|
2019
|
+
update: [permissions_1.Permissions.IsAny],
|
|
2020
|
+
},
|
|
2021
|
+
transformer: (0, transformers_1.AdminOwnerTransformer)({
|
|
2022
|
+
// Only allow 'name' to be written, so 'calories' will throw
|
|
2023
|
+
anonWriteFields: ["name"],
|
|
2024
|
+
}),
|
|
2025
|
+
}));
|
|
2026
|
+
server = (0, supertest_1.default)(app);
|
|
2027
|
+
return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ calories: 100 }).expect(403)];
|
|
2028
|
+
case 1:
|
|
2029
|
+
res = _a.sent();
|
|
2030
|
+
(0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
|
|
2031
|
+
return [2 /*return*/];
|
|
2032
|
+
}
|
|
2033
|
+
});
|
|
2034
|
+
}); });
|
|
2035
|
+
(0, bun_test_1.it)("model.create validation error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2036
|
+
var RequiredModel, res;
|
|
2037
|
+
return __generator(this, function (_a) {
|
|
2038
|
+
switch (_a.label) {
|
|
2039
|
+
case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./tests")); })];
|
|
2040
|
+
case 1:
|
|
2041
|
+
RequiredModel = (_a.sent()).RequiredModel;
|
|
2042
|
+
app.use("/required", (0, api_1.modelRouter)(RequiredModel, {
|
|
2043
|
+
allowAnonymous: true,
|
|
2044
|
+
permissions: {
|
|
2045
|
+
create: [permissions_1.Permissions.IsAny],
|
|
2046
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
2047
|
+
list: [permissions_1.Permissions.IsAny],
|
|
2048
|
+
read: [permissions_1.Permissions.IsAny],
|
|
2049
|
+
update: [permissions_1.Permissions.IsAny],
|
|
2050
|
+
},
|
|
2051
|
+
}));
|
|
2052
|
+
server = (0, supertest_1.default)(app);
|
|
2053
|
+
return [4 /*yield*/, server.post("/required").send({ about: "test" }).expect(400)];
|
|
2054
|
+
case 2:
|
|
2055
|
+
res = _a.sent();
|
|
2056
|
+
(0, bun_test_1.expect)(res.body.title).toContain("Required");
|
|
2057
|
+
return [2 /*return*/];
|
|
2058
|
+
}
|
|
2059
|
+
});
|
|
2060
|
+
}); });
|
|
2061
|
+
});
|
|
2062
|
+
(0, bun_test_1.describe)("special query params", function () {
|
|
2063
|
+
var admin;
|
|
2064
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2065
|
+
var _a;
|
|
2066
|
+
return __generator(this, function (_b) {
|
|
2067
|
+
switch (_b.label) {
|
|
2068
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
2069
|
+
case 1:
|
|
2070
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
2071
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
2072
|
+
calories: 1,
|
|
2073
|
+
created: new Date("2021-12-03T00:00:20.000Z"),
|
|
2074
|
+
hidden: false,
|
|
2075
|
+
name: "Spinach",
|
|
2076
|
+
ownerId: admin._id,
|
|
2077
|
+
})];
|
|
2078
|
+
case 2:
|
|
2079
|
+
_b.sent();
|
|
2080
|
+
app = (0, tests_1.getBaseServer)();
|
|
2081
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
2082
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
2083
|
+
return [2 /*return*/];
|
|
2084
|
+
}
|
|
2085
|
+
});
|
|
2086
|
+
}); });
|
|
2087
|
+
(0, bun_test_1.it)("period query param is stripped from query", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2088
|
+
var res;
|
|
2089
|
+
return __generator(this, function (_a) {
|
|
2090
|
+
switch (_a.label) {
|
|
2091
|
+
case 0:
|
|
2092
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
2093
|
+
allowAnonymous: true,
|
|
2094
|
+
permissions: {
|
|
2095
|
+
create: [permissions_1.Permissions.IsAny],
|
|
2096
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
2097
|
+
list: [permissions_1.Permissions.IsAny],
|
|
2098
|
+
read: [permissions_1.Permissions.IsAny],
|
|
2099
|
+
update: [permissions_1.Permissions.IsAny],
|
|
2100
|
+
},
|
|
2101
|
+
queryFields: ["name", "period"],
|
|
2102
|
+
queryFilter: function (_user, query) {
|
|
2103
|
+
// Simulate a queryFilter that accepts and processes period
|
|
2104
|
+
if (query === null || query === void 0 ? void 0 : query.period) {
|
|
2105
|
+
// Period is processed but shouldn't be passed to mongo
|
|
2106
|
+
return query;
|
|
2107
|
+
}
|
|
2108
|
+
return query !== null && query !== void 0 ? query : {};
|
|
2109
|
+
},
|
|
2110
|
+
}));
|
|
2111
|
+
server = (0, supertest_1.default)(app);
|
|
2112
|
+
return [4 /*yield*/, server.get("/food?period=weekly").expect(200)];
|
|
2113
|
+
case 1:
|
|
2114
|
+
res = _a.sent();
|
|
2115
|
+
(0, bun_test_1.expect)(res.body.data).toBeDefined();
|
|
2116
|
+
return [2 /*return*/];
|
|
2117
|
+
}
|
|
2118
|
+
});
|
|
2119
|
+
}); });
|
|
2120
|
+
(0, bun_test_1.it)("query with false value", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2121
|
+
var res;
|
|
2122
|
+
return __generator(this, function (_a) {
|
|
2123
|
+
switch (_a.label) {
|
|
2124
|
+
case 0:
|
|
2125
|
+
// Create a food that is hidden
|
|
2126
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
2127
|
+
calories: 50,
|
|
2128
|
+
created: new Date("2021-12-04T00:00:20.000Z"),
|
|
2129
|
+
hidden: true,
|
|
2130
|
+
name: "HiddenFood",
|
|
2131
|
+
ownerId: admin._id,
|
|
2132
|
+
})];
|
|
2133
|
+
case 1:
|
|
2134
|
+
// Create a food that is hidden
|
|
2135
|
+
_a.sent();
|
|
2136
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
2137
|
+
allowAnonymous: true,
|
|
2138
|
+
permissions: {
|
|
2139
|
+
create: [permissions_1.Permissions.IsAny],
|
|
2140
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
2141
|
+
list: [permissions_1.Permissions.IsAny],
|
|
2142
|
+
read: [permissions_1.Permissions.IsAny],
|
|
2143
|
+
update: [permissions_1.Permissions.IsAny],
|
|
2144
|
+
},
|
|
2145
|
+
queryFields: ["name", "hidden"],
|
|
2146
|
+
}));
|
|
2147
|
+
server = (0, supertest_1.default)(app);
|
|
2148
|
+
return [4 /*yield*/, server.get("/food?hidden=false").expect(200)];
|
|
2149
|
+
case 2:
|
|
2150
|
+
res = _a.sent();
|
|
2151
|
+
(0, bun_test_1.expect)(res.body.data.every(function (f) { return f.hidden === false; })).toBe(true);
|
|
2152
|
+
return [2 /*return*/];
|
|
2153
|
+
}
|
|
2154
|
+
});
|
|
2155
|
+
}); });
|
|
2156
|
+
(0, bun_test_1.it)("$search query triggers special handling code path", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2157
|
+
var res;
|
|
2158
|
+
return __generator(this, function (_a) {
|
|
2159
|
+
switch (_a.label) {
|
|
2160
|
+
case 0:
|
|
2161
|
+
// The $search code path just accesses the collection but doesn't do anything with it
|
|
2162
|
+
// This test verifies the code path is exercised
|
|
2163
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
2164
|
+
allowAnonymous: true,
|
|
2165
|
+
permissions: {
|
|
2166
|
+
create: [permissions_1.Permissions.IsAny],
|
|
2167
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
2168
|
+
list: [permissions_1.Permissions.IsAny],
|
|
2169
|
+
read: [permissions_1.Permissions.IsAny],
|
|
2170
|
+
update: [permissions_1.Permissions.IsAny],
|
|
2171
|
+
},
|
|
2172
|
+
// Need to include $search in queryFields for it to pass validation
|
|
2173
|
+
queryFields: ["name", "$search"],
|
|
2174
|
+
}));
|
|
2175
|
+
server = (0, supertest_1.default)(app);
|
|
2176
|
+
return [4 /*yield*/, server.get("/food?$search=test")];
|
|
2177
|
+
case 1:
|
|
2178
|
+
res = _a.sent();
|
|
2179
|
+
// May return 500 because $search is passed to Mongo which doesn't support it without Atlas
|
|
2180
|
+
// The important thing is we've exercised the code path
|
|
2181
|
+
(0, bun_test_1.expect)(res.status === 200 || res.status === 500).toBe(true);
|
|
2182
|
+
return [2 /*return*/];
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
}); });
|
|
2186
|
+
(0, bun_test_1.it)("$autocomplete query triggers special handling code path", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2187
|
+
var res;
|
|
2188
|
+
return __generator(this, function (_a) {
|
|
2189
|
+
switch (_a.label) {
|
|
2190
|
+
case 0:
|
|
2191
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
2192
|
+
allowAnonymous: true,
|
|
2193
|
+
permissions: {
|
|
2194
|
+
create: [permissions_1.Permissions.IsAny],
|
|
2195
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
2196
|
+
list: [permissions_1.Permissions.IsAny],
|
|
2197
|
+
read: [permissions_1.Permissions.IsAny],
|
|
2198
|
+
update: [permissions_1.Permissions.IsAny],
|
|
2199
|
+
},
|
|
2200
|
+
queryFields: ["name", "$autocomplete"],
|
|
2201
|
+
}));
|
|
2202
|
+
server = (0, supertest_1.default)(app);
|
|
2203
|
+
return [4 /*yield*/, server.get("/food?$autocomplete=test")];
|
|
2204
|
+
case 1:
|
|
2205
|
+
res = _a.sent();
|
|
2206
|
+
(0, bun_test_1.expect)(res.status === 200 || res.status === 500).toBe(true);
|
|
2207
|
+
return [2 /*return*/];
|
|
2208
|
+
}
|
|
2209
|
+
});
|
|
2210
|
+
}); });
|
|
2211
|
+
});
|
|
2212
|
+
(0, bun_test_1.describe)("addPopulateToQuery", function () {
|
|
2213
|
+
(0, bun_test_1.it)("returns query unchanged with no populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2214
|
+
var query, result;
|
|
2215
|
+
return __generator(this, function (_a) {
|
|
2216
|
+
switch (_a.label) {
|
|
2217
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
2218
|
+
case 1:
|
|
2219
|
+
_a.sent();
|
|
2220
|
+
query = tests_1.FoodModel.find({});
|
|
2221
|
+
result = (0, api_1.addPopulateToQuery)(query, undefined);
|
|
2222
|
+
(0, bun_test_1.expect)(result).toBe(query);
|
|
2223
|
+
return [2 /*return*/];
|
|
2224
|
+
}
|
|
2225
|
+
});
|
|
2226
|
+
}); });
|
|
2227
|
+
(0, bun_test_1.it)("returns query unchanged with empty populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2228
|
+
var query, result;
|
|
2229
|
+
return __generator(this, function (_a) {
|
|
2230
|
+
switch (_a.label) {
|
|
2231
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
2232
|
+
case 1:
|
|
2233
|
+
_a.sent();
|
|
2234
|
+
query = tests_1.FoodModel.find({});
|
|
2235
|
+
result = (0, api_1.addPopulateToQuery)(query, []);
|
|
2236
|
+
(0, bun_test_1.expect)(result).toBe(query);
|
|
2237
|
+
return [2 /*return*/];
|
|
2238
|
+
}
|
|
2239
|
+
});
|
|
2240
|
+
}); });
|
|
2241
|
+
(0, bun_test_1.it)("applies multiple populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2242
|
+
var query, result;
|
|
2243
|
+
return __generator(this, function (_a) {
|
|
2244
|
+
switch (_a.label) {
|
|
2245
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
2246
|
+
case 1:
|
|
2247
|
+
_a.sent();
|
|
2248
|
+
query = tests_1.FoodModel.find({});
|
|
2249
|
+
result = (0, api_1.addPopulateToQuery)(query, [
|
|
2250
|
+
{ fields: ["email"], path: "ownerId" },
|
|
2251
|
+
{ fields: ["name"], path: "eatenBy" },
|
|
2252
|
+
]);
|
|
2253
|
+
// The result should be a query with populate applied
|
|
2254
|
+
(0, bun_test_1.expect)(result).toBeDefined();
|
|
2255
|
+
return [2 /*return*/];
|
|
2256
|
+
}
|
|
2257
|
+
});
|
|
2258
|
+
}); });
|
|
2259
|
+
});
|
|
2260
|
+
(0, bun_test_1.describe)("soft delete with isDeleted plugin", function () {
|
|
2261
|
+
var admin;
|
|
2262
|
+
var agent;
|
|
2263
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2264
|
+
var _a;
|
|
2265
|
+
return __generator(this, function (_b) {
|
|
2266
|
+
switch (_b.label) {
|
|
2267
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
2268
|
+
case 1:
|
|
2269
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
2270
|
+
app = (0, tests_1.getBaseServer)();
|
|
2271
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
2272
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
2273
|
+
return [2 /*return*/];
|
|
2274
|
+
}
|
|
2275
|
+
});
|
|
2276
|
+
}); });
|
|
2277
|
+
(0, bun_test_1.it)("soft deletes user with deleted field", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2278
|
+
var res, deletedUser;
|
|
2279
|
+
return __generator(this, function (_a) {
|
|
2280
|
+
switch (_a.label) {
|
|
2281
|
+
case 0:
|
|
2282
|
+
// UserModel has the isDisabledPlugin which adds a 'disabled' field,
|
|
2283
|
+
// but we need to test the 'deleted' field check.
|
|
2284
|
+
// Let's use a model that has the deleted field.
|
|
2285
|
+
app.use("/users", (0, api_1.modelRouter)(tests_1.UserModel, {
|
|
2286
|
+
allowAnonymous: true,
|
|
2287
|
+
permissions: {
|
|
2288
|
+
create: [permissions_1.Permissions.IsAny],
|
|
2289
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
2290
|
+
list: [permissions_1.Permissions.IsAny],
|
|
2291
|
+
read: [permissions_1.Permissions.IsAny],
|
|
2292
|
+
update: [permissions_1.Permissions.IsAny],
|
|
2293
|
+
},
|
|
2294
|
+
}));
|
|
2295
|
+
server = (0, supertest_1.default)(app);
|
|
2296
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
2297
|
+
case 1:
|
|
2298
|
+
agent = _a.sent();
|
|
2299
|
+
return [4 /*yield*/, agent.delete("/users/".concat(admin._id)).expect(204)];
|
|
2300
|
+
case 2:
|
|
2301
|
+
res = _a.sent();
|
|
2302
|
+
(0, bun_test_1.expect)(res.body).toEqual({});
|
|
2303
|
+
return [4 /*yield*/, tests_1.UserModel.findById(admin._id)];
|
|
2304
|
+
case 3:
|
|
2305
|
+
deletedUser = _a.sent();
|
|
2306
|
+
(0, bun_test_1.expect)(deletedUser).toBeNull();
|
|
2307
|
+
return [2 /*return*/];
|
|
2308
|
+
}
|
|
2309
|
+
});
|
|
2310
|
+
}); });
|
|
2311
|
+
});
|
|
2312
|
+
(0, bun_test_1.describe)("populate in create", function () {
|
|
2313
|
+
var admin;
|
|
2314
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2315
|
+
var _a;
|
|
2316
|
+
return __generator(this, function (_b) {
|
|
2317
|
+
switch (_b.label) {
|
|
2318
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
2319
|
+
case 1:
|
|
2320
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
2321
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
2322
|
+
calories: 1,
|
|
2323
|
+
created: new Date("2021-12-03T00:00:20.000Z"),
|
|
2324
|
+
hidden: false,
|
|
2325
|
+
name: "Spinach",
|
|
2326
|
+
ownerId: admin._id,
|
|
2327
|
+
})];
|
|
2328
|
+
case 2:
|
|
2329
|
+
_b.sent();
|
|
2330
|
+
app = (0, tests_1.getBaseServer)();
|
|
2331
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
2332
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
2333
|
+
return [2 /*return*/];
|
|
2334
|
+
}
|
|
2335
|
+
});
|
|
2336
|
+
}); });
|
|
2337
|
+
(0, bun_test_1.it)("handles populate with valid path in create", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2338
|
+
var res;
|
|
2339
|
+
return __generator(this, function (_a) {
|
|
2340
|
+
switch (_a.label) {
|
|
2341
|
+
case 0:
|
|
2342
|
+
// Test that valid populate works in create flow
|
|
2343
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
2344
|
+
allowAnonymous: true,
|
|
2345
|
+
permissions: {
|
|
2346
|
+
create: [permissions_1.Permissions.IsAny],
|
|
2347
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
2348
|
+
list: [permissions_1.Permissions.IsAny],
|
|
2349
|
+
read: [permissions_1.Permissions.IsAny],
|
|
2350
|
+
update: [permissions_1.Permissions.IsAny],
|
|
2351
|
+
},
|
|
2352
|
+
populatePaths: [{ fields: ["email"], path: "ownerId" }],
|
|
2353
|
+
}));
|
|
2354
|
+
server = (0, supertest_1.default)(app);
|
|
2355
|
+
return [4 /*yield*/, server
|
|
2356
|
+
.post("/food")
|
|
2357
|
+
.send({ calories: 15, name: "Broccoli", ownerId: admin._id })
|
|
2358
|
+
.expect(201)];
|
|
2359
|
+
case 1:
|
|
2360
|
+
res = _a.sent();
|
|
2361
|
+
(0, bun_test_1.expect)(res.body.data.name).toBe("Broccoli");
|
|
2362
|
+
// Verify populate worked - ownerId should be an object with email
|
|
2363
|
+
(0, bun_test_1.expect)(res.body.data.ownerId.email).toBe(admin.email);
|
|
2364
|
+
return [2 /*return*/];
|
|
2365
|
+
}
|
|
2366
|
+
});
|
|
2367
|
+
}); });
|
|
2368
|
+
});
|
|
2369
|
+
(0, bun_test_1.describe)("save error handling", function () {
|
|
2370
|
+
var admin;
|
|
2371
|
+
var spinach;
|
|
2372
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2373
|
+
var _a;
|
|
2374
|
+
return __generator(this, function (_b) {
|
|
2375
|
+
switch (_b.label) {
|
|
2376
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
2377
|
+
case 1:
|
|
2378
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
2379
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
2380
|
+
calories: 1,
|
|
2381
|
+
created: new Date("2021-12-03T00:00:20.000Z"),
|
|
2382
|
+
hidden: false,
|
|
2383
|
+
name: "Spinach",
|
|
2384
|
+
ownerId: admin._id,
|
|
2385
|
+
source: {
|
|
2386
|
+
name: "Brand",
|
|
2387
|
+
},
|
|
2388
|
+
})];
|
|
2389
|
+
case 2:
|
|
2390
|
+
spinach = _b.sent();
|
|
2391
|
+
app = (0, tests_1.getBaseServer)();
|
|
2392
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
2393
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
2394
|
+
return [2 /*return*/];
|
|
2395
|
+
}
|
|
2396
|
+
});
|
|
2397
|
+
}); });
|
|
2398
|
+
(0, bun_test_1.it)("handles patch save error with validation failure", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2399
|
+
var res;
|
|
2400
|
+
return __generator(this, function (_a) {
|
|
2401
|
+
switch (_a.label) {
|
|
2402
|
+
case 0:
|
|
2403
|
+
// The FoodModel has strict: "throw" which will cause validation errors for unknown fields
|
|
2404
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
2405
|
+
allowAnonymous: true,
|
|
2406
|
+
permissions: {
|
|
2407
|
+
create: [permissions_1.Permissions.IsAny],
|
|
2408
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
2409
|
+
list: [permissions_1.Permissions.IsAny],
|
|
2410
|
+
read: [permissions_1.Permissions.IsAny],
|
|
2411
|
+
update: [permissions_1.Permissions.IsAny],
|
|
2412
|
+
},
|
|
2413
|
+
}));
|
|
2414
|
+
server = (0, supertest_1.default)(app);
|
|
2415
|
+
return [4 /*yield*/, server
|
|
2416
|
+
.patch("/food/".concat(spinach._id))
|
|
2417
|
+
.send({ invalidField: "value" })
|
|
2418
|
+
.expect(400)];
|
|
2419
|
+
case 1:
|
|
2420
|
+
res = _a.sent();
|
|
2421
|
+
(0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook save error");
|
|
2422
|
+
return [2 /*return*/];
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2425
|
+
}); });
|
|
2426
|
+
});
|
|
2427
|
+
(0, bun_test_1.describe)("body undefined after transform without preCreate", function () {
|
|
2428
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2429
|
+
return __generator(this, function (_a) {
|
|
2430
|
+
switch (_a.label) {
|
|
2431
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
2432
|
+
case 1:
|
|
2433
|
+
_a.sent();
|
|
2434
|
+
app = (0, tests_1.getBaseServer)();
|
|
2435
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
2436
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
2437
|
+
return [2 /*return*/];
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
}); });
|
|
2441
|
+
(0, bun_test_1.it)("handles undefined body after transform when no preCreate", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2442
|
+
var res;
|
|
2443
|
+
return __generator(this, function (_a) {
|
|
2444
|
+
switch (_a.label) {
|
|
2445
|
+
case 0:
|
|
2446
|
+
// Create a transformer that returns undefined
|
|
2447
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
2448
|
+
allowAnonymous: true,
|
|
2449
|
+
permissions: {
|
|
2450
|
+
create: [permissions_1.Permissions.IsAny],
|
|
2451
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
2452
|
+
list: [permissions_1.Permissions.IsAny],
|
|
2453
|
+
read: [permissions_1.Permissions.IsAny],
|
|
2454
|
+
update: [permissions_1.Permissions.IsAny],
|
|
2455
|
+
},
|
|
2456
|
+
transformer: {
|
|
2457
|
+
transform: function () { return undefined; },
|
|
2458
|
+
},
|
|
2459
|
+
}));
|
|
2460
|
+
server = (0, supertest_1.default)(app);
|
|
2461
|
+
return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
|
|
2462
|
+
case 1:
|
|
2463
|
+
res = _a.sent();
|
|
2464
|
+
(0, bun_test_1.expect)(res.body.title).toBe("Invalid request body");
|
|
2465
|
+
(0, bun_test_1.expect)(res.body.detail).toBe("Body is undefined");
|
|
2466
|
+
return [2 /*return*/];
|
|
2467
|
+
}
|
|
2468
|
+
});
|
|
2469
|
+
}); });
|
|
2470
|
+
});
|
|
2471
|
+
(0, bun_test_1.describe)("soft delete with deleted field", function () {
|
|
2472
|
+
var _admin;
|
|
2473
|
+
var agent;
|
|
2474
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2475
|
+
var _a;
|
|
2476
|
+
return __generator(this, function (_b) {
|
|
2477
|
+
switch (_b.label) {
|
|
2478
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
2479
|
+
case 1:
|
|
2480
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), _admin = _a[0];
|
|
2481
|
+
app = (0, tests_1.getBaseServer)();
|
|
2482
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
2483
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
2484
|
+
return [2 /*return*/];
|
|
2485
|
+
}
|
|
2486
|
+
});
|
|
2487
|
+
}); });
|
|
2488
|
+
(0, bun_test_1.it)("soft deletes document with deleted field using isDeletedPlugin", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2489
|
+
var mongoose, softDeleteSchema, SoftDeleteModel, testDoc, softDeleted;
|
|
2490
|
+
return __generator(this, function (_a) {
|
|
2491
|
+
switch (_a.label) {
|
|
2492
|
+
case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("mongoose")); })];
|
|
2493
|
+
case 1:
|
|
2494
|
+
mongoose = _a.sent();
|
|
2495
|
+
softDeleteSchema = new mongoose.Schema({
|
|
2496
|
+
deleted: { default: false, type: Boolean },
|
|
2497
|
+
name: String,
|
|
2498
|
+
});
|
|
2499
|
+
try {
|
|
2500
|
+
SoftDeleteModel = mongoose.model("SoftDeleteTest");
|
|
2501
|
+
}
|
|
2502
|
+
catch (_b) {
|
|
2503
|
+
SoftDeleteModel = mongoose.model("SoftDeleteTest", softDeleteSchema);
|
|
2504
|
+
}
|
|
2505
|
+
// Clean up any existing documents
|
|
2506
|
+
return [4 /*yield*/, SoftDeleteModel.deleteMany({})];
|
|
2507
|
+
case 2:
|
|
2508
|
+
// Clean up any existing documents
|
|
2509
|
+
_a.sent();
|
|
2510
|
+
return [4 /*yield*/, SoftDeleteModel.create({ name: "TestItem" })];
|
|
2511
|
+
case 3:
|
|
2512
|
+
testDoc = _a.sent();
|
|
2513
|
+
app.use("/softdelete", (0, api_1.modelRouter)(SoftDeleteModel, {
|
|
2514
|
+
allowAnonymous: true,
|
|
2515
|
+
permissions: {
|
|
2516
|
+
create: [permissions_1.Permissions.IsAny],
|
|
2517
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
2518
|
+
list: [permissions_1.Permissions.IsAny],
|
|
2519
|
+
read: [permissions_1.Permissions.IsAny],
|
|
2520
|
+
update: [permissions_1.Permissions.IsAny],
|
|
2521
|
+
},
|
|
2522
|
+
}));
|
|
2523
|
+
server = (0, supertest_1.default)(app);
|
|
2524
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
2525
|
+
case 4:
|
|
2526
|
+
agent = _a.sent();
|
|
2527
|
+
// Delete should soft delete (set deleted: true) instead of hard delete
|
|
2528
|
+
return [4 /*yield*/, agent.delete("/softdelete/".concat(testDoc._id)).expect(204)];
|
|
2529
|
+
case 5:
|
|
2530
|
+
// Delete should soft delete (set deleted: true) instead of hard delete
|
|
2531
|
+
_a.sent();
|
|
2532
|
+
return [4 /*yield*/, SoftDeleteModel.findById(testDoc._id)];
|
|
2533
|
+
case 6:
|
|
2534
|
+
softDeleted = _a.sent();
|
|
2535
|
+
(0, bun_test_1.expect)(softDeleted).not.toBeNull();
|
|
2536
|
+
(0, bun_test_1.expect)(softDeleted === null || softDeleted === void 0 ? void 0 : softDeleted.deleted).toBe(true);
|
|
2537
|
+
// Clean up
|
|
2538
|
+
return [4 /*yield*/, SoftDeleteModel.deleteMany({})];
|
|
2539
|
+
case 7:
|
|
2540
|
+
// Clean up
|
|
2541
|
+
_a.sent();
|
|
2542
|
+
return [2 /*return*/];
|
|
2543
|
+
}
|
|
2544
|
+
});
|
|
2545
|
+
}); });
|
|
2546
|
+
});
|
|
2547
|
+
(0, bun_test_1.describe)("array operation with undefined preUpdate return", function () {
|
|
2548
|
+
var admin;
|
|
2549
|
+
var apple;
|
|
2550
|
+
var agent;
|
|
2551
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2552
|
+
var _a;
|
|
2553
|
+
return __generator(this, function (_b) {
|
|
2554
|
+
switch (_b.label) {
|
|
2555
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
2556
|
+
case 1:
|
|
2557
|
+
_a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
|
|
2558
|
+
return [4 /*yield*/, tests_1.FoodModel.create({
|
|
2559
|
+
calories: 100,
|
|
2560
|
+
categories: [
|
|
2561
|
+
{ name: "Fruit", show: true },
|
|
2562
|
+
{ name: "Popular", show: false },
|
|
2563
|
+
],
|
|
2564
|
+
created: new Date("2021-12-03T00:00:30.000Z"),
|
|
2565
|
+
hidden: false,
|
|
2566
|
+
name: "Apple",
|
|
2567
|
+
ownerId: admin._id,
|
|
2568
|
+
tags: ["healthy", "cheap"],
|
|
2569
|
+
})];
|
|
2570
|
+
case 2:
|
|
2571
|
+
apple = _b.sent();
|
|
2572
|
+
app = (0, tests_1.getBaseServer)();
|
|
2573
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
2574
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
2575
|
+
return [2 /*return*/];
|
|
2576
|
+
}
|
|
2577
|
+
});
|
|
2578
|
+
}); });
|
|
2579
|
+
(0, bun_test_1.it)("array operation preUpdate returning undefined for array POST throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2580
|
+
var res;
|
|
2581
|
+
return __generator(this, function (_a) {
|
|
2582
|
+
switch (_a.label) {
|
|
2583
|
+
case 0:
|
|
2584
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
2585
|
+
allowAnonymous: true,
|
|
2586
|
+
permissions: {
|
|
2587
|
+
create: [permissions_1.Permissions.IsAdmin],
|
|
2588
|
+
delete: [permissions_1.Permissions.IsAdmin],
|
|
2589
|
+
list: [permissions_1.Permissions.IsAdmin],
|
|
2590
|
+
read: [permissions_1.Permissions.IsAdmin],
|
|
2591
|
+
update: [permissions_1.Permissions.IsAdmin],
|
|
2592
|
+
},
|
|
2593
|
+
preUpdate: function () { return undefined; },
|
|
2594
|
+
}));
|
|
2595
|
+
server = (0, supertest_1.default)(app);
|
|
2596
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
|
|
2597
|
+
case 1:
|
|
2598
|
+
agent = _a.sent();
|
|
2599
|
+
return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
|
|
2600
|
+
case 2:
|
|
2601
|
+
res = _a.sent();
|
|
2602
|
+
(0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
|
|
2603
|
+
(0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preUpdate");
|
|
2604
|
+
return [2 /*return*/];
|
|
2605
|
+
}
|
|
2606
|
+
});
|
|
2607
|
+
}); });
|
|
2608
|
+
(0, bun_test_1.it)("array operation preUpdate returning null for array PATCH throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2609
|
+
var res;
|
|
2610
|
+
return __generator(this, function (_a) {
|
|
2611
|
+
switch (_a.label) {
|
|
2612
|
+
case 0:
|
|
2613
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
2614
|
+
allowAnonymous: true,
|
|
2615
|
+
permissions: {
|
|
2616
|
+
create: [permissions_1.Permissions.IsAdmin],
|
|
2617
|
+
delete: [permissions_1.Permissions.IsAdmin],
|
|
2618
|
+
list: [permissions_1.Permissions.IsAdmin],
|
|
2619
|
+
read: [permissions_1.Permissions.IsAdmin],
|
|
2620
|
+
update: [permissions_1.Permissions.IsAdmin],
|
|
2621
|
+
},
|
|
2622
|
+
preUpdate: function () { return null; },
|
|
2623
|
+
}));
|
|
2624
|
+
server = (0, supertest_1.default)(app);
|
|
2625
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
|
|
2626
|
+
case 1:
|
|
2627
|
+
agent = _a.sent();
|
|
2628
|
+
return [4 /*yield*/, agent
|
|
2629
|
+
.patch("/food/".concat(apple._id, "/tags/healthy"))
|
|
2630
|
+
.send({ tags: "unhealthy" })
|
|
2631
|
+
.expect(403)];
|
|
2632
|
+
case 2:
|
|
2633
|
+
res = _a.sent();
|
|
2634
|
+
(0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
|
|
2635
|
+
return [2 /*return*/];
|
|
2636
|
+
}
|
|
2637
|
+
});
|
|
2638
|
+
}); });
|
|
2639
|
+
(0, bun_test_1.it)("array operation preUpdate error for array DELETE is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
2640
|
+
var res;
|
|
2641
|
+
return __generator(this, function (_a) {
|
|
2642
|
+
switch (_a.label) {
|
|
2643
|
+
case 0:
|
|
2644
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
2645
|
+
allowAnonymous: true,
|
|
2646
|
+
permissions: {
|
|
2647
|
+
create: [permissions_1.Permissions.IsAdmin],
|
|
2648
|
+
delete: [permissions_1.Permissions.IsAdmin],
|
|
2649
|
+
list: [permissions_1.Permissions.IsAdmin],
|
|
2650
|
+
read: [permissions_1.Permissions.IsAdmin],
|
|
2651
|
+
update: [permissions_1.Permissions.IsAdmin],
|
|
2652
|
+
},
|
|
2653
|
+
preUpdate: function () {
|
|
2654
|
+
throw new Error("preUpdate error during delete");
|
|
2655
|
+
},
|
|
2656
|
+
}));
|
|
2657
|
+
server = (0, supertest_1.default)(app);
|
|
2658
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
|
|
2659
|
+
case 1:
|
|
2660
|
+
agent = _a.sent();
|
|
2661
|
+
return [4 /*yield*/, agent.delete("/food/".concat(apple._id, "/tags/healthy")).expect(400)];
|
|
2662
|
+
case 2:
|
|
2663
|
+
res = _a.sent();
|
|
2664
|
+
(0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook error");
|
|
2665
|
+
return [2 /*return*/];
|
|
2666
|
+
}
|
|
2667
|
+
});
|
|
2668
|
+
}); });
|
|
1155
2669
|
});
|
|
1156
2670
|
});
|