@terreno/api 0.0.11-beta.1 → 0.0.11

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
@@ -90,6 +90,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
90
90
  Object.defineProperty(exports, "__esModule", { value: true });
91
91
  var bun_test_1 = require("bun:test");
92
92
  var Sentry = __importStar(require("@sentry/node"));
93
+ var sortBy_1 = __importDefault(require("lodash/sortBy"));
93
94
  var qs_1 = __importDefault(require("qs"));
94
95
  var supertest_1 = __importDefault(require("supertest"));
95
96
  var api_1 = require("./api");
@@ -98,7 +99,6 @@ var errors_1 = require("./errors");
98
99
  var expressServer_1 = require("./expressServer");
99
100
  var permissions_1 = require("./permissions");
100
101
  var tests_1 = require("./tests");
101
- var transformers_1 = require("./transformers");
102
102
  (0, bun_test_1.describe)("@terreno/api", function () {
103
103
  var server;
104
104
  var app;
@@ -1879,2147 +1879,262 @@ var transformers_1 = require("./transformers");
1879
1879
  });
1880
1880
  }); });
1881
1881
  });
1882
- (0, bun_test_1.describe)("error handling", function () {
1883
- var admin;
1884
- var agent;
1885
- var spinach;
1886
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
1887
- var _a;
1888
- return __generator(this, function (_b) {
1889
- switch (_b.label) {
1890
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
1891
- case 1:
1892
- _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
1893
- return [4 /*yield*/, tests_1.FoodModel.create({
1894
- calories: 1,
1895
- created: new Date("2021-12-03T00:00:20.000Z"),
1896
- hidden: false,
1897
- name: "Spinach",
1898
- ownerId: admin._id,
1899
- source: {
1900
- name: "Brand",
1901
- },
1902
- })];
1903
- case 2:
1904
- spinach = _b.sent();
1905
- app = (0, tests_1.getBaseServer)();
1906
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
1907
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
1908
- return [2 /*return*/];
1909
- }
1910
- });
1911
- }); });
1912
- (0, bun_test_1.it)("PUT returns 500 not supported", function () { return __awaiter(void 0, void 0, void 0, function () {
1913
- var res;
1914
- return __generator(this, function (_a) {
1915
- switch (_a.label) {
1916
- case 0:
1917
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1918
- allowAnonymous: true,
1919
- permissions: {
1920
- create: [permissions_1.Permissions.IsAny],
1921
- delete: [permissions_1.Permissions.IsAny],
1922
- list: [permissions_1.Permissions.IsAny],
1923
- read: [permissions_1.Permissions.IsAny],
1924
- update: [permissions_1.Permissions.IsAny],
1925
- },
1926
- }));
1927
- server = (0, supertest_1.default)(app);
1928
- return [4 /*yield*/, server.put("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(500)];
1929
- case 1:
1930
- res = _a.sent();
1931
- (0, bun_test_1.expect)(res.body.title).toBe("PUT is not supported.");
1932
- return [2 /*return*/];
1933
- }
1934
- });
1935
- }); });
1936
- (0, bun_test_1.it)("preCreate returning undefined throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
1937
- var res;
1938
- return __generator(this, function (_a) {
1939
- switch (_a.label) {
1940
- case 0:
1941
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1942
- allowAnonymous: true,
1943
- permissions: {
1944
- create: [permissions_1.Permissions.IsAny],
1945
- delete: [permissions_1.Permissions.IsAny],
1946
- list: [permissions_1.Permissions.IsAny],
1947
- read: [permissions_1.Permissions.IsAny],
1948
- update: [permissions_1.Permissions.IsAny],
1949
- },
1950
- preCreate: function () { return undefined; },
1951
- }));
1952
- server = (0, supertest_1.default)(app);
1953
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(403)];
1954
- case 1:
1955
- res = _a.sent();
1956
- (0, bun_test_1.expect)(res.body.title).toBe("Create not allowed");
1957
- (0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preCreate");
1958
- return [2 /*return*/];
1959
- }
1960
- });
1961
- }); });
1962
- (0, bun_test_1.it)("preUpdate returning undefined throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
1963
- var res;
1964
- return __generator(this, function (_a) {
1965
- switch (_a.label) {
1966
- case 0:
1967
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1968
- allowAnonymous: true,
1969
- permissions: {
1970
- create: [permissions_1.Permissions.IsAny],
1971
- delete: [permissions_1.Permissions.IsAny],
1972
- list: [permissions_1.Permissions.IsAny],
1973
- read: [permissions_1.Permissions.IsAny],
1974
- update: [permissions_1.Permissions.IsAny],
1975
- },
1976
- preUpdate: function () { return undefined; },
1977
- }));
1978
- server = (0, supertest_1.default)(app);
1979
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(403)];
1980
- case 1:
1981
- res = _a.sent();
1982
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
1983
- (0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preUpdate");
1984
- return [2 /*return*/];
1985
- }
1986
- });
1987
- }); });
1988
- (0, bun_test_1.it)("preDelete returning undefined throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
1989
- var res;
1990
- return __generator(this, function (_a) {
1991
- switch (_a.label) {
1992
- case 0:
1993
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
1994
- allowAnonymous: true,
1995
- permissions: {
1996
- create: [permissions_1.Permissions.IsAny],
1997
- delete: [permissions_1.Permissions.IsAny],
1998
- list: [permissions_1.Permissions.IsAny],
1999
- read: [permissions_1.Permissions.IsAny],
2000
- update: [permissions_1.Permissions.IsAny],
2001
- },
2002
- preDelete: function () { return undefined; },
2003
- }));
2004
- server = (0, supertest_1.default)(app);
2005
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
2006
- case 1:
2007
- agent = _a.sent();
2008
- return [4 /*yield*/, agent.delete("/food/".concat(spinach._id)).expect(403)];
2009
- case 2:
2010
- res = _a.sent();
2011
- (0, bun_test_1.expect)(res.body.title).toBe("Delete not allowed");
2012
- (0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preDelete");
2013
- return [2 /*return*/];
2014
- }
2015
- });
2016
- }); });
2017
- (0, bun_test_1.it)("postCreate hook error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2018
- var res;
2019
- return __generator(this, function (_a) {
2020
- switch (_a.label) {
2021
- case 0:
2022
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2023
- allowAnonymous: true,
2024
- permissions: {
2025
- create: [permissions_1.Permissions.IsAny],
2026
- delete: [permissions_1.Permissions.IsAny],
2027
- list: [permissions_1.Permissions.IsAny],
2028
- read: [permissions_1.Permissions.IsAny],
2029
- update: [permissions_1.Permissions.IsAny],
2030
- },
2031
- postCreate: function () {
2032
- throw new Error("postCreate failed");
2033
- },
2034
- }));
2035
- server = (0, supertest_1.default)(app);
2036
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
2037
- case 1:
2038
- res = _a.sent();
2039
- (0, bun_test_1.expect)(res.body.title).toContain("postCreate hook error");
2040
- return [2 /*return*/];
2041
- }
2042
- });
2043
- }); });
2044
- (0, bun_test_1.it)("postUpdate hook error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2045
- var res;
2046
- return __generator(this, function (_a) {
2047
- switch (_a.label) {
2048
- case 0:
2049
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2050
- allowAnonymous: true,
2051
- permissions: {
2052
- create: [permissions_1.Permissions.IsAny],
2053
- delete: [permissions_1.Permissions.IsAny],
2054
- list: [permissions_1.Permissions.IsAny],
2055
- read: [permissions_1.Permissions.IsAny],
2056
- update: [permissions_1.Permissions.IsAny],
2057
- },
2058
- postUpdate: function () {
2059
- throw new Error("postUpdate failed");
2060
- },
2061
- }));
2062
- server = (0, supertest_1.default)(app);
2063
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(400)];
2064
- case 1:
2065
- res = _a.sent();
2066
- (0, bun_test_1.expect)(res.body.title).toContain("postUpdate hook error");
2067
- return [2 /*return*/];
2068
- }
2069
- });
2070
- }); });
2071
- (0, bun_test_1.it)("postDelete hook error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2072
- var res;
2073
- return __generator(this, function (_a) {
2074
- switch (_a.label) {
2075
- case 0:
2076
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2077
- allowAnonymous: true,
2078
- permissions: {
2079
- create: [permissions_1.Permissions.IsAny],
2080
- delete: [permissions_1.Permissions.IsAny],
2081
- list: [permissions_1.Permissions.IsAny],
2082
- read: [permissions_1.Permissions.IsAny],
2083
- update: [permissions_1.Permissions.IsAny],
2084
- },
2085
- postDelete: function () {
2086
- throw new Error("postDelete failed");
2087
- },
2088
- }));
2089
- server = (0, supertest_1.default)(app);
2090
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
2091
- case 1:
2092
- agent = _a.sent();
2093
- return [4 /*yield*/, agent.delete("/food/".concat(spinach._id)).expect(400)];
2094
- case 2:
2095
- res = _a.sent();
2096
- (0, bun_test_1.expect)(res.body.title).toContain("postDelete hook error");
2097
- return [2 /*return*/];
2098
- }
2099
- });
2100
- }); });
2101
- (0, bun_test_1.it)("responseHandler error in read is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2102
- var res;
2103
- return __generator(this, function (_a) {
2104
- switch (_a.label) {
2105
- case 0:
2106
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2107
- allowAnonymous: true,
2108
- permissions: {
2109
- create: [permissions_1.Permissions.IsAny],
2110
- delete: [permissions_1.Permissions.IsAny],
2111
- list: [permissions_1.Permissions.IsAny],
2112
- read: [permissions_1.Permissions.IsAny],
2113
- update: [permissions_1.Permissions.IsAny],
2114
- },
2115
- responseHandler: function (_data, method) {
2116
- if (method === "read") {
2117
- throw new Error("responseHandler read failed");
2118
- }
2119
- return {};
2120
- },
2121
- }));
2122
- server = (0, supertest_1.default)(app);
2123
- return [4 /*yield*/, server.get("/food/".concat(spinach._id)).expect(500)];
2124
- case 1:
2125
- res = _a.sent();
2126
- (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2127
- return [2 /*return*/];
2128
- }
2129
- });
2130
- }); });
2131
- (0, bun_test_1.it)("responseHandler error in create is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2132
- var res;
2133
- return __generator(this, function (_a) {
2134
- switch (_a.label) {
2135
- case 0:
2136
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2137
- allowAnonymous: true,
2138
- permissions: {
2139
- create: [permissions_1.Permissions.IsAny],
2140
- delete: [permissions_1.Permissions.IsAny],
2141
- list: [permissions_1.Permissions.IsAny],
2142
- read: [permissions_1.Permissions.IsAny],
2143
- update: [permissions_1.Permissions.IsAny],
2144
- },
2145
- responseHandler: function (_data, method) {
2146
- if (method === "create") {
2147
- throw new Error("responseHandler create failed");
2148
- }
2149
- return {};
2150
- },
2151
- }));
2152
- server = (0, supertest_1.default)(app);
2153
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(500)];
2154
- case 1:
2155
- res = _a.sent();
2156
- (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2157
- return [2 /*return*/];
2158
- }
2159
- });
2160
- }); });
2161
- (0, bun_test_1.it)("responseHandler error in update is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2162
- var res;
2163
- return __generator(this, function (_a) {
2164
- switch (_a.label) {
2165
- case 0:
2166
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2167
- allowAnonymous: true,
2168
- permissions: {
2169
- create: [permissions_1.Permissions.IsAny],
2170
- delete: [permissions_1.Permissions.IsAny],
2171
- list: [permissions_1.Permissions.IsAny],
2172
- read: [permissions_1.Permissions.IsAny],
2173
- update: [permissions_1.Permissions.IsAny],
2174
- },
2175
- responseHandler: function (_data, method) {
2176
- if (method === "update") {
2177
- throw new Error("responseHandler update failed");
2178
- }
2179
- return {};
2180
- },
2181
- }));
2182
- server = (0, supertest_1.default)(app);
2183
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(500)];
2184
- case 1:
2185
- res = _a.sent();
2186
- (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2187
- return [2 /*return*/];
2188
- }
2189
- });
2190
- }); });
2191
- (0, bun_test_1.it)("responseHandler error in list is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2192
- var res;
2193
- return __generator(this, function (_a) {
2194
- switch (_a.label) {
2195
- case 0:
2196
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2197
- allowAnonymous: true,
2198
- permissions: {
2199
- create: [permissions_1.Permissions.IsAny],
2200
- delete: [permissions_1.Permissions.IsAny],
2201
- list: [permissions_1.Permissions.IsAny],
2202
- read: [permissions_1.Permissions.IsAny],
2203
- update: [permissions_1.Permissions.IsAny],
2204
- },
2205
- responseHandler: function (_data, method) {
2206
- if (method === "list") {
2207
- throw new Error("responseHandler list failed");
2208
- }
2209
- return {};
2210
- },
2211
- }));
2212
- server = (0, supertest_1.default)(app);
2213
- return [4 /*yield*/, server.get("/food").expect(500)];
2214
- case 1:
2215
- res = _a.sent();
2216
- (0, bun_test_1.expect)(res.body.title).toContain("responseHandler error");
2217
- return [2 /*return*/];
2218
- }
2219
- });
2220
- }); });
2221
- (0, bun_test_1.it)("list with non-array responseHandler returns data directly", function () { return __awaiter(void 0, void 0, void 0, function () {
2222
- var res;
2223
- return __generator(this, function (_a) {
2224
- switch (_a.label) {
2225
- case 0:
2226
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2227
- allowAnonymous: true,
2228
- permissions: {
2229
- create: [permissions_1.Permissions.IsAny],
2230
- delete: [permissions_1.Permissions.IsAny],
2231
- list: [permissions_1.Permissions.IsAny],
2232
- read: [permissions_1.Permissions.IsAny],
2233
- update: [permissions_1.Permissions.IsAny],
2234
- },
2235
- responseHandler: function (_data, method) {
2236
- if (method === "list") {
2237
- return { custom: "response" };
2238
- }
2239
- return {};
2240
- },
2241
- }));
2242
- server = (0, supertest_1.default)(app);
2243
- return [4 /*yield*/, server.get("/food").expect(200)];
2244
- case 1:
2245
- res = _a.sent();
2246
- (0, bun_test_1.expect)(res.body.data).toEqual({ custom: "response" });
2247
- (0, bun_test_1.expect)(res.body.more).toBeUndefined();
2248
- (0, bun_test_1.expect)(res.body.total).toBeUndefined();
2249
- return [2 /*return*/];
2250
- }
2251
- });
2252
- }); });
2253
- (0, bun_test_1.it)("list with query sort param", function () { return __awaiter(void 0, void 0, void 0, function () {
2254
- var res;
2255
- return __generator(this, function (_a) {
2256
- switch (_a.label) {
2257
- case 0: return [4 /*yield*/, tests_1.FoodModel.create({
2258
- calories: 200,
2259
- created: new Date("2021-12-04T00:00:20.000Z"),
2260
- hidden: false,
2261
- name: "Apple",
2262
- ownerId: admin._id,
2263
- })];
2264
- case 1:
2265
- _a.sent();
2266
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2267
- allowAnonymous: true,
2268
- permissions: {
2269
- create: [permissions_1.Permissions.IsAny],
2270
- delete: [permissions_1.Permissions.IsAny],
2271
- list: [permissions_1.Permissions.IsAny],
2272
- read: [permissions_1.Permissions.IsAny],
2273
- update: [permissions_1.Permissions.IsAny],
2274
- },
2275
- queryFields: ["name"],
2276
- }));
2277
- server = (0, supertest_1.default)(app);
2278
- return [4 /*yield*/, server.get("/food?sort=name").expect(200)];
2279
- case 2:
2280
- res = _a.sent();
2281
- (0, bun_test_1.expect)(res.body.data[0].name).toBe("Apple");
2282
- (0, bun_test_1.expect)(res.body.data[1].name).toBe("Spinach");
2283
- return [4 /*yield*/, server.get("/food?sort=-name").expect(200)];
2284
- case 3:
2285
- // Sort by name descending
2286
- res = _a.sent();
2287
- (0, bun_test_1.expect)(res.body.data[0].name).toBe("Spinach");
2288
- (0, bun_test_1.expect)(res.body.data[1].name).toBe("Apple");
2289
- return [2 /*return*/];
2290
- }
2291
- });
2292
- }); });
2293
- (0, bun_test_1.it)("queryFilter error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2294
- var res;
2295
- return __generator(this, function (_a) {
2296
- switch (_a.label) {
2297
- case 0:
2298
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2299
- allowAnonymous: true,
2300
- permissions: {
2301
- create: [permissions_1.Permissions.IsAny],
2302
- delete: [permissions_1.Permissions.IsAny],
2303
- list: [permissions_1.Permissions.IsAny],
2304
- read: [permissions_1.Permissions.IsAny],
2305
- update: [permissions_1.Permissions.IsAny],
2306
- },
2307
- queryFilter: function () {
2308
- throw new Error("queryFilter failed");
2309
- },
2310
- }));
2311
- server = (0, supertest_1.default)(app);
2312
- return [4 /*yield*/, server.get("/food").expect(400)];
2313
- case 1:
2314
- res = _a.sent();
2315
- (0, bun_test_1.expect)(res.body.title).toContain("Query filter error");
2316
- return [2 /*return*/];
2317
- }
2318
- });
2319
- }); });
2320
- (0, bun_test_1.it)("custom endpoints take priority", function () { return __awaiter(void 0, void 0, void 0, function () {
2321
- var res;
2322
- return __generator(this, function (_a) {
2323
- switch (_a.label) {
2324
- case 0:
2325
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2326
- allowAnonymous: true,
2327
- endpoints: function (router) {
2328
- router.get("/custom", function (_req, res) {
2329
- res.json({ custom: true });
2330
- });
2331
- },
2332
- permissions: {
2333
- create: [permissions_1.Permissions.IsAny],
2334
- delete: [permissions_1.Permissions.IsAny],
2335
- list: [permissions_1.Permissions.IsAny],
2336
- read: [permissions_1.Permissions.IsAny],
2337
- update: [permissions_1.Permissions.IsAny],
2338
- },
2339
- }));
2340
- server = (0, supertest_1.default)(app);
2341
- return [4 /*yield*/, server.get("/food/custom").expect(200)];
2342
- case 1:
2343
- res = _a.sent();
2344
- (0, bun_test_1.expect)(res.body.custom).toBe(true);
2345
- return [2 /*return*/];
2346
- }
2347
- });
2348
- }); });
2349
- (0, bun_test_1.it)("disallowed query param returns 400", function () { return __awaiter(void 0, void 0, void 0, function () {
2350
- var res;
2351
- return __generator(this, function (_a) {
2352
- switch (_a.label) {
2353
- case 0:
2354
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2355
- allowAnonymous: true,
2356
- permissions: {
2357
- create: [permissions_1.Permissions.IsAny],
2358
- delete: [permissions_1.Permissions.IsAny],
2359
- list: [permissions_1.Permissions.IsAny],
2360
- read: [permissions_1.Permissions.IsAny],
2361
- update: [permissions_1.Permissions.IsAny],
2362
- },
2363
- queryFields: ["name"],
2364
- }));
2365
- server = (0, supertest_1.default)(app);
2366
- return [4 /*yield*/, server.get("/food?calories=100").expect(400)];
2367
- case 1:
2368
- res = _a.sent();
2369
- (0, bun_test_1.expect)(res.body.title).toContain("calories is not allowed as a query param");
2370
- return [2 /*return*/];
2371
- }
2372
- });
2373
- }); });
2374
- (0, bun_test_1.it)("queryFilter returning null returns empty array", function () { return __awaiter(void 0, void 0, void 0, function () {
2375
- var res;
2376
- return __generator(this, function (_a) {
2377
- switch (_a.label) {
2378
- case 0:
2379
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2380
- allowAnonymous: true,
2381
- permissions: {
2382
- create: [permissions_1.Permissions.IsAny],
2383
- delete: [permissions_1.Permissions.IsAny],
2384
- list: [permissions_1.Permissions.IsAny],
2385
- read: [permissions_1.Permissions.IsAny],
2386
- update: [permissions_1.Permissions.IsAny],
2387
- },
2388
- queryFilter: function () { return null; },
2389
- }));
2390
- server = (0, supertest_1.default)(app);
2391
- return [4 /*yield*/, server.get("/food").expect(200)];
2392
- case 1:
2393
- res = _a.sent();
2394
- (0, bun_test_1.expect)(res.body.data).toEqual([]);
2395
- return [2 /*return*/];
2396
- }
2397
- });
2398
- }); });
2399
- (0, bun_test_1.it)("preUpdate returning null throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
2400
- var res;
2401
- return __generator(this, function (_a) {
2402
- switch (_a.label) {
2403
- case 0:
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
- preUpdate: function () { return null; },
2414
- }));
2415
- server = (0, supertest_1.default)(app);
2416
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(403)];
2417
- case 1:
2418
- res = _a.sent();
2419
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
2420
- return [2 /*return*/];
2421
- }
2422
- });
2423
- }); });
2424
- (0, bun_test_1.it)("preDelete returning null throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
2425
- var res;
2426
- return __generator(this, function (_a) {
2427
- switch (_a.label) {
2428
- case 0:
2429
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2430
- allowAnonymous: true,
2431
- permissions: {
2432
- create: [permissions_1.Permissions.IsAny],
2433
- delete: [permissions_1.Permissions.IsAny],
2434
- list: [permissions_1.Permissions.IsAny],
2435
- read: [permissions_1.Permissions.IsAny],
2436
- update: [permissions_1.Permissions.IsAny],
2437
- },
2438
- preDelete: function () { return null; },
2439
- }));
2440
- server = (0, supertest_1.default)(app);
2441
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
2442
- case 1:
2443
- agent = _a.sent();
2444
- return [4 /*yield*/, agent.delete("/food/".concat(spinach._id)).expect(403)];
2445
- case 2:
2446
- res = _a.sent();
2447
- (0, bun_test_1.expect)(res.body.title).toBe("Delete not allowed");
2448
- return [2 /*return*/];
2449
- }
2450
- });
2451
- }); });
2452
- (0, bun_test_1.it)("preCreate returning null throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
2453
- var res;
2454
- return __generator(this, function (_a) {
2455
- switch (_a.label) {
2456
- case 0:
2457
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2458
- allowAnonymous: true,
2459
- permissions: {
2460
- create: [permissions_1.Permissions.IsAny],
2461
- delete: [permissions_1.Permissions.IsAny],
2462
- list: [permissions_1.Permissions.IsAny],
2463
- read: [permissions_1.Permissions.IsAny],
2464
- update: [permissions_1.Permissions.IsAny],
2465
- },
2466
- preCreate: function () { return null; },
2467
- }));
2468
- server = (0, supertest_1.default)(app);
2469
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(403)];
2470
- case 1:
2471
- res = _a.sent();
2472
- (0, bun_test_1.expect)(res.body.title).toBe("Create not allowed");
2473
- return [2 /*return*/];
2474
- }
2475
- });
2476
- }); });
2477
- (0, bun_test_1.it)("preCreate error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2478
- var res;
2479
- return __generator(this, function (_a) {
2480
- switch (_a.label) {
2481
- case 0:
2482
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2483
- allowAnonymous: true,
2484
- permissions: {
2485
- create: [permissions_1.Permissions.IsAny],
2486
- delete: [permissions_1.Permissions.IsAny],
2487
- list: [permissions_1.Permissions.IsAny],
2488
- read: [permissions_1.Permissions.IsAny],
2489
- update: [permissions_1.Permissions.IsAny],
2490
- },
2491
- preCreate: function () {
2492
- throw new Error("preCreate failed");
2493
- },
2494
- }));
2495
- server = (0, supertest_1.default)(app);
2496
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
2497
- case 1:
2498
- res = _a.sent();
2499
- (0, bun_test_1.expect)(res.body.title).toContain("preCreate hook error");
2500
- return [2 /*return*/];
2501
- }
2502
- });
2503
- }); });
2504
- (0, bun_test_1.it)("preUpdate error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2505
- var res;
2506
- return __generator(this, function (_a) {
2507
- switch (_a.label) {
2508
- case 0:
2509
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2510
- allowAnonymous: true,
2511
- permissions: {
2512
- create: [permissions_1.Permissions.IsAny],
2513
- delete: [permissions_1.Permissions.IsAny],
2514
- list: [permissions_1.Permissions.IsAny],
2515
- read: [permissions_1.Permissions.IsAny],
2516
- update: [permissions_1.Permissions.IsAny],
2517
- },
2518
- preUpdate: function () {
2519
- throw new Error("preUpdate failed");
2520
- },
2521
- }));
2522
- server = (0, supertest_1.default)(app);
2523
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ name: "Kale" }).expect(400)];
2524
- case 1:
2525
- res = _a.sent();
2526
- (0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook error");
2527
- return [2 /*return*/];
2528
- }
2529
- });
2530
- }); });
2531
- (0, bun_test_1.it)("invalid array operation type returns 400", function () { return __awaiter(void 0, void 0, void 0, function () {
2532
- return __generator(this, function (_a) {
2533
- return [2 /*return*/];
2534
- });
2535
- }); });
2536
- });
2537
- (0, bun_test_1.describe)("array operation errors", function () {
2538
- var admin;
2539
- var apple;
2540
- var agent;
2541
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
2542
- var _a;
2543
- return __generator(this, function (_b) {
2544
- switch (_b.label) {
2545
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
2546
- case 1:
2547
- _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
2548
- return [4 /*yield*/, tests_1.FoodModel.create({
2549
- calories: 100,
2550
- categories: [
2551
- { name: "Fruit", show: true },
2552
- { name: "Popular", show: false },
2553
- ],
2554
- created: new Date("2021-12-03T00:00:30.000Z"),
2555
- hidden: false,
2556
- name: "Apple",
2557
- ownerId: admin._id,
2558
- tags: ["healthy", "cheap"],
2559
- })];
2560
- case 2:
2561
- apple = _b.sent();
2562
- app = (0, tests_1.getBaseServer)();
2563
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
2564
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
2565
- return [2 /*return*/];
2566
- }
2567
- });
2568
- }); });
2569
- (0, bun_test_1.it)("array operation preUpdate returning undefined throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
2570
- var res;
2571
- return __generator(this, function (_a) {
2572
- switch (_a.label) {
2573
- case 0:
2574
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2575
- allowAnonymous: true,
2576
- permissions: {
2577
- create: [permissions_1.Permissions.IsAdmin],
2578
- delete: [permissions_1.Permissions.IsAdmin],
2579
- list: [permissions_1.Permissions.IsAdmin],
2580
- read: [permissions_1.Permissions.IsAdmin],
2581
- update: [permissions_1.Permissions.IsAdmin],
2582
- },
2583
- preUpdate: function () { return undefined; },
2584
- }));
2585
- server = (0, supertest_1.default)(app);
2586
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
2587
- case 1:
2588
- agent = _a.sent();
2589
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
2590
- case 2:
2591
- res = _a.sent();
2592
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
2593
- (0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preUpdate");
2594
- return [2 /*return*/];
2595
- }
2596
- });
2597
- }); });
2598
- (0, bun_test_1.it)("array operation preUpdate returning null throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
2599
- var res;
2600
- return __generator(this, function (_a) {
2601
- switch (_a.label) {
2602
- case 0:
2603
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2604
- allowAnonymous: true,
2605
- permissions: {
2606
- create: [permissions_1.Permissions.IsAdmin],
2607
- delete: [permissions_1.Permissions.IsAdmin],
2608
- list: [permissions_1.Permissions.IsAdmin],
2609
- read: [permissions_1.Permissions.IsAdmin],
2610
- update: [permissions_1.Permissions.IsAdmin],
2611
- },
2612
- preUpdate: function () { return null; },
2613
- }));
2614
- server = (0, supertest_1.default)(app);
2615
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
2616
- case 1:
2617
- agent = _a.sent();
2618
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
2619
- case 2:
2620
- res = _a.sent();
2621
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
2622
- return [2 /*return*/];
2623
- }
2624
- });
2625
- }); });
2626
- (0, bun_test_1.it)("array operation preUpdate error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2627
- var res;
2628
- return __generator(this, function (_a) {
2629
- switch (_a.label) {
2630
- case 0:
2631
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2632
- allowAnonymous: true,
2633
- permissions: {
2634
- create: [permissions_1.Permissions.IsAdmin],
2635
- delete: [permissions_1.Permissions.IsAdmin],
2636
- list: [permissions_1.Permissions.IsAdmin],
2637
- read: [permissions_1.Permissions.IsAdmin],
2638
- update: [permissions_1.Permissions.IsAdmin],
2639
- },
2640
- preUpdate: function () {
2641
- throw new Error("preUpdate array failed");
2642
- },
2643
- }));
2644
- server = (0, supertest_1.default)(app);
2645
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
2646
- case 1:
2647
- agent = _a.sent();
2648
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(400)];
2649
- case 2:
2650
- res = _a.sent();
2651
- (0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook error");
2652
- return [2 /*return*/];
2653
- }
2654
- });
2655
- }); });
2656
- (0, bun_test_1.it)("array operation postUpdate error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2657
- var res;
2658
- return __generator(this, function (_a) {
2659
- switch (_a.label) {
2660
- case 0:
2661
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2662
- allowAnonymous: true,
2663
- permissions: {
2664
- create: [permissions_1.Permissions.IsAdmin],
2665
- delete: [permissions_1.Permissions.IsAdmin],
2666
- list: [permissions_1.Permissions.IsAdmin],
2667
- read: [permissions_1.Permissions.IsAdmin],
2668
- update: [permissions_1.Permissions.IsAdmin],
2669
- },
2670
- postUpdate: function () {
2671
- throw new Error("postUpdate array failed");
2672
- },
2673
- }));
2674
- server = (0, supertest_1.default)(app);
2675
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
2676
- case 1:
2677
- agent = _a.sent();
2678
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(400)];
2679
- case 2:
2680
- res = _a.sent();
2681
- (0, bun_test_1.expect)(res.body.title).toContain("PATCH Post Update error");
2682
- return [2 /*return*/];
2683
- }
2684
- });
2685
- }); });
2686
- (0, bun_test_1.it)("array operation denied without update permission", function () { return __awaiter(void 0, void 0, void 0, function () {
2687
- var res;
2688
- return __generator(this, function (_a) {
2689
- switch (_a.label) {
2690
- case 0:
2691
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2692
- allowAnonymous: true,
2693
- permissions: {
2694
- create: [permissions_1.Permissions.IsAdmin],
2695
- delete: [permissions_1.Permissions.IsAdmin],
2696
- list: [permissions_1.Permissions.IsAny],
2697
- read: [permissions_1.Permissions.IsAny],
2698
- update: [permissions_1.Permissions.IsAdmin],
2699
- },
2700
- }));
2701
- server = (0, supertest_1.default)(app);
2702
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
2703
- case 1:
2704
- agent = _a.sent();
2705
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(405)];
2706
- case 2:
2707
- res = _a.sent();
2708
- (0, bun_test_1.expect)(res.body.title).toContain("Access to PATCH");
2709
- return [2 /*return*/];
2710
- }
2711
- });
2712
- }); });
2713
- (0, bun_test_1.it)("array operation on non-existent document returns 404", function () { return __awaiter(void 0, void 0, void 0, function () {
2714
- var fakeId, res;
2715
- return __generator(this, function (_a) {
2716
- switch (_a.label) {
2717
- case 0:
2718
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2719
- allowAnonymous: true,
2720
- permissions: {
2721
- create: [permissions_1.Permissions.IsAdmin],
2722
- delete: [permissions_1.Permissions.IsAdmin],
2723
- list: [permissions_1.Permissions.IsAdmin],
2724
- read: [permissions_1.Permissions.IsAdmin],
2725
- update: [permissions_1.Permissions.IsAdmin],
2726
- },
2727
- }));
2728
- server = (0, supertest_1.default)(app);
2729
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
2730
- case 1:
2731
- agent = _a.sent();
2732
- fakeId = "000000000000000000000000";
2733
- return [4 /*yield*/, agent.post("/food/".concat(fakeId, "/tags")).send({ tags: "organic" }).expect(404)];
2734
- case 2:
2735
- res = _a.sent();
2736
- (0, bun_test_1.expect)(res.body.title).toContain("Could not find document to PATCH");
2737
- return [2 /*return*/];
2738
- }
2739
- });
2740
- }); });
2741
- (0, bun_test_1.it)("array operation denied when user cannot update specific doc", function () { return __awaiter(void 0, void 0, void 0, function () {
2742
- var res;
2743
- return __generator(this, function (_a) {
2744
- switch (_a.label) {
2745
- case 0:
2746
- // Create food owned by admin, then try to update as notAdmin
2747
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2748
- allowAnonymous: true,
2749
- permissions: {
2750
- create: [permissions_1.Permissions.IsAuthenticated],
2751
- delete: [permissions_1.Permissions.IsAuthenticated],
2752
- list: [permissions_1.Permissions.IsAuthenticated],
2753
- read: [permissions_1.Permissions.IsAuthenticated],
2754
- update: [permissions_1.Permissions.IsOwner],
2755
- },
2756
- }));
2757
- server = (0, supertest_1.default)(app);
2758
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
2759
- case 1:
2760
- // Login as notAdmin and try to update admin's food (apple)
2761
- agent = _a.sent();
2762
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
2763
- case 2:
2764
- res = _a.sent();
2765
- (0, bun_test_1.expect)(res.body.title).toContain("Patch not allowed");
2766
- return [2 /*return*/];
2767
- }
2768
- });
2769
- }); });
2770
- (0, bun_test_1.it)("array operation transform error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2771
- var res;
2772
- return __generator(this, function (_a) {
2773
- switch (_a.label) {
2774
- case 0:
2775
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2776
- allowAnonymous: true,
2777
- permissions: {
2778
- create: [permissions_1.Permissions.IsAdmin],
2779
- delete: [permissions_1.Permissions.IsAdmin],
2780
- list: [permissions_1.Permissions.IsAdmin],
2781
- read: [permissions_1.Permissions.IsAdmin],
2782
- update: [permissions_1.Permissions.IsAdmin],
2783
- },
2784
- transformer: (0, transformers_1.AdminOwnerTransformer)({
2785
- adminWriteFields: ["name"],
2786
- }),
2787
- }));
2788
- server = (0, supertest_1.default)(app);
2789
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
2790
- case 1:
2791
- agent = _a.sent();
2792
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
2793
- case 2:
2794
- res = _a.sent();
2795
- (0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
2796
- return [2 /*return*/];
2797
- }
2798
- });
2799
- }); });
2800
- });
2801
- (0, bun_test_1.describe)("transformer errors", function () {
2802
- var admin;
2803
- var spinach;
2804
- var agent;
2805
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
2806
- var _a;
2807
- return __generator(this, function (_b) {
2808
- switch (_b.label) {
2809
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
2810
- case 1:
2811
- _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
2812
- return [4 /*yield*/, tests_1.FoodModel.create({
2813
- calories: 1,
2814
- created: new Date("2021-12-03T00:00:20.000Z"),
2815
- hidden: false,
2816
- name: "Spinach",
2817
- ownerId: admin._id,
2818
- source: {
2819
- name: "Brand",
2820
- },
2821
- })];
2822
- case 2:
2823
- spinach = _b.sent();
2824
- app = (0, tests_1.getBaseServer)();
2825
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
2826
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
2827
- return [2 /*return*/];
2828
- }
2829
- });
2830
- }); });
2831
- (0, bun_test_1.it)("transform error in create is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2832
- var res;
2833
- return __generator(this, function (_a) {
2834
- switch (_a.label) {
2835
- case 0:
2836
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2837
- allowAnonymous: true,
2838
- permissions: {
2839
- create: [permissions_1.Permissions.IsAny],
2840
- delete: [permissions_1.Permissions.IsAny],
2841
- list: [permissions_1.Permissions.IsAny],
2842
- read: [permissions_1.Permissions.IsAny],
2843
- update: [permissions_1.Permissions.IsAny],
2844
- },
2845
- transformer: (0, transformers_1.AdminOwnerTransformer)({
2846
- // Only allow 'name' to be written, so 'calories' will throw
2847
- anonWriteFields: ["name"],
2848
- }),
2849
- }));
2850
- server = (0, supertest_1.default)(app);
2851
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
2852
- case 1:
2853
- res = _a.sent();
2854
- (0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
2855
- return [2 /*return*/];
2856
- }
2857
- });
2858
- }); });
2859
- (0, bun_test_1.it)("transform error in patch is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2860
- var res;
2861
- return __generator(this, function (_a) {
2862
- switch (_a.label) {
2863
- case 0:
2864
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2865
- allowAnonymous: true,
2866
- permissions: {
2867
- create: [permissions_1.Permissions.IsAny],
2868
- delete: [permissions_1.Permissions.IsAny],
2869
- list: [permissions_1.Permissions.IsAny],
2870
- read: [permissions_1.Permissions.IsAny],
2871
- update: [permissions_1.Permissions.IsAny],
2872
- },
2873
- transformer: (0, transformers_1.AdminOwnerTransformer)({
2874
- // Only allow 'name' to be written, so 'calories' will throw
2875
- anonWriteFields: ["name"],
2876
- }),
2877
- }));
2878
- server = (0, supertest_1.default)(app);
2879
- return [4 /*yield*/, server.patch("/food/".concat(spinach._id)).send({ calories: 100 }).expect(403)];
2880
- case 1:
2881
- res = _a.sent();
2882
- (0, bun_test_1.expect)(res.body.title).toContain("cannot write fields");
2883
- return [2 /*return*/];
2884
- }
2885
- });
2886
- }); });
2887
- (0, bun_test_1.it)("model.create validation error is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
2888
- var RequiredModel, res;
2889
- return __generator(this, function (_a) {
2890
- switch (_a.label) {
2891
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./tests")); })];
2892
- case 1:
2893
- RequiredModel = (_a.sent()).RequiredModel;
2894
- app.use("/required", (0, api_1.modelRouter)(RequiredModel, {
2895
- allowAnonymous: true,
2896
- permissions: {
2897
- create: [permissions_1.Permissions.IsAny],
2898
- delete: [permissions_1.Permissions.IsAny],
2899
- list: [permissions_1.Permissions.IsAny],
2900
- read: [permissions_1.Permissions.IsAny],
2901
- update: [permissions_1.Permissions.IsAny],
2902
- },
2903
- }));
2904
- server = (0, supertest_1.default)(app);
2905
- return [4 /*yield*/, server.post("/required").send({ about: "test" }).expect(400)];
2906
- case 2:
2907
- res = _a.sent();
2908
- (0, bun_test_1.expect)(res.body.title).toContain("Required");
2909
- return [2 /*return*/];
2910
- }
2911
- });
2912
- }); });
2913
- (0, bun_test_1.it)("preDelete hook throwing APIError is re-thrown", function () { return __awaiter(void 0, void 0, void 0, function () {
2914
- var res;
2915
- return __generator(this, function (_a) {
2916
- switch (_a.label) {
2917
- case 0:
2918
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2919
- allowAnonymous: true,
2920
- permissions: {
2921
- create: [permissions_1.Permissions.IsAny],
2922
- delete: [permissions_1.Permissions.IsAny],
2923
- list: [permissions_1.Permissions.IsAny],
2924
- read: [permissions_1.Permissions.IsAny],
2925
- update: [permissions_1.Permissions.IsAny],
2926
- },
2927
- preDelete: function () {
2928
- throw new errors_1.APIError({
2929
- disableExternalErrorTracking: true,
2930
- status: 400,
2931
- title: "Custom preDelete APIError",
2932
- });
2933
- },
2934
- }));
2935
- server = (0, supertest_1.default)(app);
2936
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
2937
- case 1:
2938
- agent = _a.sent();
2939
- return [4 /*yield*/, agent.delete("/food/".concat(spinach._id)).expect(400)];
2940
- case 2:
2941
- res = _a.sent();
2942
- (0, bun_test_1.expect)(res.body.title).toBe("Custom preDelete APIError");
2943
- (0, bun_test_1.expect)(res.body.disableExternalErrorTracking).toBe(true);
2944
- return [2 /*return*/];
2945
- }
2946
- });
2947
- }); });
2948
- });
2949
- (0, bun_test_1.describe)("special query params", function () {
2950
- var admin;
2951
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
2952
- var _a;
2953
- return __generator(this, function (_b) {
2954
- switch (_b.label) {
2955
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
2956
- case 1:
2957
- _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
2958
- return [4 /*yield*/, tests_1.FoodModel.create({
2959
- calories: 1,
2960
- created: new Date("2021-12-03T00:00:20.000Z"),
2961
- hidden: false,
2962
- name: "Spinach",
2963
- ownerId: admin._id,
2964
- })];
2965
- case 2:
2966
- _b.sent();
2967
- app = (0, tests_1.getBaseServer)();
2968
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
2969
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
2970
- return [2 /*return*/];
2971
- }
2972
- });
2973
- }); });
2974
- (0, bun_test_1.it)("period query param is stripped from query", function () { return __awaiter(void 0, void 0, void 0, function () {
2975
- var res;
2976
- return __generator(this, function (_a) {
2977
- switch (_a.label) {
2978
- case 0:
2979
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
2980
- allowAnonymous: true,
2981
- permissions: {
2982
- create: [permissions_1.Permissions.IsAny],
2983
- delete: [permissions_1.Permissions.IsAny],
2984
- list: [permissions_1.Permissions.IsAny],
2985
- read: [permissions_1.Permissions.IsAny],
2986
- update: [permissions_1.Permissions.IsAny],
2987
- },
2988
- queryFields: ["name", "period"],
2989
- queryFilter: function (_user, query) {
2990
- // Simulate a queryFilter that accepts and processes period
2991
- if (query === null || query === void 0 ? void 0 : query.period) {
2992
- // Period is processed but shouldn't be passed to mongo
2993
- return query;
2994
- }
2995
- return query !== null && query !== void 0 ? query : {};
2996
- },
2997
- }));
2998
- server = (0, supertest_1.default)(app);
2999
- return [4 /*yield*/, server.get("/food?period=weekly").expect(200)];
3000
- case 1:
3001
- res = _a.sent();
3002
- (0, bun_test_1.expect)(res.body.data).toBeDefined();
3003
- return [2 /*return*/];
3004
- }
3005
- });
3006
- }); });
3007
- (0, bun_test_1.it)("query with false value", function () { return __awaiter(void 0, void 0, void 0, function () {
3008
- var res;
3009
- return __generator(this, function (_a) {
3010
- switch (_a.label) {
3011
- case 0:
3012
- // Create a food that is hidden
3013
- return [4 /*yield*/, tests_1.FoodModel.create({
3014
- calories: 50,
3015
- created: new Date("2021-12-04T00:00:20.000Z"),
3016
- hidden: true,
3017
- name: "HiddenFood",
3018
- ownerId: admin._id,
3019
- })];
3020
- case 1:
3021
- // Create a food that is hidden
3022
- _a.sent();
3023
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3024
- allowAnonymous: true,
3025
- permissions: {
3026
- create: [permissions_1.Permissions.IsAny],
3027
- delete: [permissions_1.Permissions.IsAny],
3028
- list: [permissions_1.Permissions.IsAny],
3029
- read: [permissions_1.Permissions.IsAny],
3030
- update: [permissions_1.Permissions.IsAny],
3031
- },
3032
- queryFields: ["name", "hidden"],
3033
- }));
3034
- server = (0, supertest_1.default)(app);
3035
- return [4 /*yield*/, server.get("/food?hidden=false").expect(200)];
3036
- case 2:
3037
- res = _a.sent();
3038
- (0, bun_test_1.expect)(res.body.data.every(function (f) { return f.hidden === false; })).toBe(true);
3039
- return [2 /*return*/];
3040
- }
3041
- });
3042
- }); });
3043
- (0, bun_test_1.it)("$search query triggers special handling code path", function () { return __awaiter(void 0, void 0, void 0, function () {
3044
- var res;
3045
- return __generator(this, function (_a) {
3046
- switch (_a.label) {
3047
- case 0:
3048
- // The $search code path just accesses the collection but doesn't do anything with it
3049
- // This test verifies the code path is exercised
3050
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3051
- allowAnonymous: true,
3052
- permissions: {
3053
- create: [permissions_1.Permissions.IsAny],
3054
- delete: [permissions_1.Permissions.IsAny],
3055
- list: [permissions_1.Permissions.IsAny],
3056
- read: [permissions_1.Permissions.IsAny],
3057
- update: [permissions_1.Permissions.IsAny],
3058
- },
3059
- // Need to include $search in queryFields for it to pass validation
3060
- queryFields: ["name", "$search"],
3061
- }));
3062
- server = (0, supertest_1.default)(app);
3063
- return [4 /*yield*/, server.get("/food?$search=test")];
3064
- case 1:
3065
- res = _a.sent();
3066
- // May return 500 because $search is passed to Mongo which doesn't support it without Atlas
3067
- // The important thing is we've exercised the code path
3068
- (0, bun_test_1.expect)(res.status === 200 || res.status === 500).toBe(true);
3069
- return [2 /*return*/];
3070
- }
3071
- });
3072
- }); });
3073
- (0, bun_test_1.it)("$autocomplete query triggers special handling code path", function () { return __awaiter(void 0, void 0, void 0, function () {
3074
- var res;
3075
- return __generator(this, function (_a) {
3076
- switch (_a.label) {
3077
- case 0:
3078
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3079
- allowAnonymous: true,
3080
- permissions: {
3081
- create: [permissions_1.Permissions.IsAny],
3082
- delete: [permissions_1.Permissions.IsAny],
3083
- list: [permissions_1.Permissions.IsAny],
3084
- read: [permissions_1.Permissions.IsAny],
3085
- update: [permissions_1.Permissions.IsAny],
3086
- },
3087
- queryFields: ["name", "$autocomplete"],
3088
- }));
3089
- server = (0, supertest_1.default)(app);
3090
- return [4 /*yield*/, server.get("/food?$autocomplete=test")];
3091
- case 1:
3092
- res = _a.sent();
3093
- (0, bun_test_1.expect)(res.status === 200 || res.status === 500).toBe(true);
3094
- return [2 /*return*/];
3095
- }
3096
- });
3097
- }); });
3098
- });
3099
- (0, bun_test_1.describe)("addPopulateToQuery", function () {
3100
- (0, bun_test_1.it)("returns query unchanged with no populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
3101
- var query, result;
3102
- return __generator(this, function (_a) {
3103
- switch (_a.label) {
3104
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
3105
- case 1:
3106
- _a.sent();
3107
- query = tests_1.FoodModel.find({});
3108
- result = (0, api_1.addPopulateToQuery)(query, undefined);
3109
- (0, bun_test_1.expect)(result).toBe(query);
3110
- return [2 /*return*/];
3111
- }
3112
- });
3113
- }); });
3114
- (0, bun_test_1.it)("returns query unchanged with empty populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
3115
- var query, result;
3116
- return __generator(this, function (_a) {
3117
- switch (_a.label) {
3118
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
3119
- case 1:
3120
- _a.sent();
3121
- query = tests_1.FoodModel.find({});
3122
- result = (0, api_1.addPopulateToQuery)(query, []);
3123
- (0, bun_test_1.expect)(result).toBe(query);
3124
- return [2 /*return*/];
3125
- }
3126
- });
3127
- }); });
3128
- (0, bun_test_1.it)("applies multiple populate paths", function () { return __awaiter(void 0, void 0, void 0, function () {
3129
- var query, result;
3130
- return __generator(this, function (_a) {
3131
- switch (_a.label) {
3132
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
3133
- case 1:
3134
- _a.sent();
3135
- query = tests_1.FoodModel.find({});
3136
- result = (0, api_1.addPopulateToQuery)(query, [
3137
- { fields: ["email"], path: "ownerId" },
3138
- { fields: ["name"], path: "eatenBy" },
3139
- ]);
3140
- // The result should be a query with populate applied
3141
- (0, bun_test_1.expect)(result).toBeDefined();
3142
- return [2 /*return*/];
3143
- }
3144
- });
3145
- }); });
3146
- });
3147
- (0, bun_test_1.describe)("soft delete with isDeleted plugin", function () {
3148
- var admin;
3149
- var agent;
3150
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
3151
- var _a;
3152
- return __generator(this, function (_b) {
3153
- switch (_b.label) {
3154
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
3155
- case 1:
3156
- _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
3157
- app = (0, tests_1.getBaseServer)();
3158
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
3159
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
3160
- return [2 /*return*/];
3161
- }
3162
- });
3163
- }); });
3164
- (0, bun_test_1.it)("soft deletes user with deleted field", function () { return __awaiter(void 0, void 0, void 0, function () {
3165
- var res, deletedUser;
3166
- return __generator(this, function (_a) {
3167
- switch (_a.label) {
3168
- case 0:
3169
- // UserModel has the isDisabledPlugin which adds a 'disabled' field,
3170
- // but we need to test the 'deleted' field check.
3171
- // Let's use a model that has the deleted field.
3172
- app.use("/users", (0, api_1.modelRouter)(tests_1.UserModel, {
3173
- allowAnonymous: true,
3174
- permissions: {
3175
- create: [permissions_1.Permissions.IsAny],
3176
- delete: [permissions_1.Permissions.IsAny],
3177
- list: [permissions_1.Permissions.IsAny],
3178
- read: [permissions_1.Permissions.IsAny],
3179
- update: [permissions_1.Permissions.IsAny],
3180
- },
3181
- }));
3182
- server = (0, supertest_1.default)(app);
3183
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
3184
- case 1:
3185
- agent = _a.sent();
3186
- return [4 /*yield*/, agent.delete("/users/".concat(admin._id)).expect(204)];
3187
- case 2:
3188
- res = _a.sent();
3189
- (0, bun_test_1.expect)(res.body).toEqual({});
3190
- return [4 /*yield*/, tests_1.UserModel.findById(admin._id)];
3191
- case 3:
3192
- deletedUser = _a.sent();
3193
- (0, bun_test_1.expect)(deletedUser).toBeNull();
3194
- return [2 /*return*/];
3195
- }
3196
- });
3197
- }); });
3198
- });
3199
- (0, bun_test_1.describe)("populate in create", function () {
3200
- var admin;
3201
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
3202
- var _a;
3203
- return __generator(this, function (_b) {
3204
- switch (_b.label) {
3205
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
3206
- case 1:
3207
- _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
3208
- return [4 /*yield*/, tests_1.FoodModel.create({
3209
- calories: 1,
3210
- created: new Date("2021-12-03T00:00:20.000Z"),
3211
- hidden: false,
3212
- name: "Spinach",
3213
- ownerId: admin._id,
3214
- })];
3215
- case 2:
3216
- _b.sent();
3217
- app = (0, tests_1.getBaseServer)();
3218
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
3219
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
3220
- return [2 /*return*/];
3221
- }
3222
- });
3223
- }); });
3224
- (0, bun_test_1.it)("handles populate with valid path in create", function () { return __awaiter(void 0, void 0, void 0, function () {
3225
- var res;
3226
- return __generator(this, function (_a) {
3227
- switch (_a.label) {
3228
- case 0:
3229
- // Test that valid populate works in create flow
3230
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3231
- allowAnonymous: true,
3232
- permissions: {
3233
- create: [permissions_1.Permissions.IsAny],
3234
- delete: [permissions_1.Permissions.IsAny],
3235
- list: [permissions_1.Permissions.IsAny],
3236
- read: [permissions_1.Permissions.IsAny],
3237
- update: [permissions_1.Permissions.IsAny],
3238
- },
3239
- populatePaths: [{ fields: ["email"], path: "ownerId" }],
3240
- }));
3241
- server = (0, supertest_1.default)(app);
3242
- return [4 /*yield*/, server
3243
- .post("/food")
3244
- .send({ calories: 15, name: "Broccoli", ownerId: admin._id })
3245
- .expect(201)];
3246
- case 1:
3247
- res = _a.sent();
3248
- (0, bun_test_1.expect)(res.body.data.name).toBe("Broccoli");
3249
- // Verify populate worked - ownerId should be an object with email
3250
- (0, bun_test_1.expect)(res.body.data.ownerId.email).toBe(admin.email);
3251
- return [2 /*return*/];
3252
- }
3253
- });
3254
- }); });
3255
- });
3256
- (0, bun_test_1.describe)("save error handling", function () {
3257
- var admin;
3258
- var spinach;
3259
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
3260
- var _a;
3261
- return __generator(this, function (_b) {
3262
- switch (_b.label) {
3263
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
3264
- case 1:
3265
- _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
3266
- return [4 /*yield*/, tests_1.FoodModel.create({
3267
- calories: 1,
3268
- created: new Date("2021-12-03T00:00:20.000Z"),
3269
- hidden: false,
3270
- name: "Spinach",
3271
- ownerId: admin._id,
3272
- source: {
3273
- name: "Brand",
3274
- },
3275
- })];
3276
- case 2:
3277
- spinach = _b.sent();
3278
- app = (0, tests_1.getBaseServer)();
3279
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
3280
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
3281
- return [2 /*return*/];
3282
- }
3283
- });
3284
- }); });
3285
- (0, bun_test_1.it)("handles patch save error with validation failure", function () { return __awaiter(void 0, void 0, void 0, function () {
3286
- var res;
3287
- return __generator(this, function (_a) {
3288
- switch (_a.label) {
3289
- case 0:
3290
- // The FoodModel has strict: "throw" which will cause validation errors for unknown fields
3291
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3292
- allowAnonymous: true,
3293
- permissions: {
3294
- create: [permissions_1.Permissions.IsAny],
3295
- delete: [permissions_1.Permissions.IsAny],
3296
- list: [permissions_1.Permissions.IsAny],
3297
- read: [permissions_1.Permissions.IsAny],
3298
- update: [permissions_1.Permissions.IsAny],
3299
- },
3300
- }));
3301
- server = (0, supertest_1.default)(app);
3302
- return [4 /*yield*/, server
3303
- .patch("/food/".concat(spinach._id))
3304
- .send({ invalidField: "value" })
3305
- .expect(400)];
3306
- case 1:
3307
- res = _a.sent();
3308
- (0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook save error");
3309
- return [2 /*return*/];
3310
- }
3311
- });
3312
- }); });
3313
- });
3314
- (0, bun_test_1.describe)("body undefined after transform without preCreate", function () {
3315
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
3316
- return __generator(this, function (_a) {
3317
- switch (_a.label) {
3318
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
3319
- case 1:
3320
- _a.sent();
3321
- app = (0, tests_1.getBaseServer)();
3322
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
3323
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
3324
- return [2 /*return*/];
3325
- }
3326
- });
3327
- }); });
3328
- (0, bun_test_1.it)("handles undefined body after transform when no preCreate", function () { return __awaiter(void 0, void 0, void 0, function () {
3329
- var res;
3330
- return __generator(this, function (_a) {
3331
- switch (_a.label) {
3332
- case 0:
3333
- // Create a transformer that returns undefined
3334
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3335
- allowAnonymous: true,
3336
- permissions: {
3337
- create: [permissions_1.Permissions.IsAny],
3338
- delete: [permissions_1.Permissions.IsAny],
3339
- list: [permissions_1.Permissions.IsAny],
3340
- read: [permissions_1.Permissions.IsAny],
3341
- update: [permissions_1.Permissions.IsAny],
3342
- },
3343
- transformer: {
3344
- transform: function () { return undefined; },
3345
- },
3346
- }));
3347
- server = (0, supertest_1.default)(app);
3348
- return [4 /*yield*/, server.post("/food").send({ calories: 15, name: "Broccoli" }).expect(400)];
3349
- case 1:
3350
- res = _a.sent();
3351
- (0, bun_test_1.expect)(res.body.title).toBe("Invalid request body");
3352
- (0, bun_test_1.expect)(res.body.detail).toBe("Body is undefined");
3353
- return [2 /*return*/];
3354
- }
3355
- });
3356
- }); });
3357
- });
3358
- (0, bun_test_1.describe)("soft delete with deleted field", function () {
3359
- var _admin;
1882
+ (0, bun_test_1.describe)("discriminator", function () {
1883
+ var superUser;
1884
+ var staffUser;
1885
+ var notAdmin;
3360
1886
  var agent;
3361
1887
  (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
3362
- var _a;
3363
- return __generator(this, function (_b) {
3364
- switch (_b.label) {
1888
+ var _a, staffUserId, superUserId;
1889
+ var _b;
1890
+ return __generator(this, function (_c) {
1891
+ switch (_c.label) {
3365
1892
  case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
3366
1893
  case 1:
3367
- _a = __read.apply(void 0, [_b.sent(), 1]), _admin = _a[0];
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());
3368
1913
  app = (0, tests_1.getBaseServer)();
3369
1914
  (0, auth_1.setupAuth)(app, tests_1.UserModel);
3370
1915
  (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
3371
- return [2 /*return*/];
3372
- }
3373
- });
3374
- }); });
3375
- (0, bun_test_1.it)("soft deletes document with deleted field using isDeletedPlugin", function () { return __awaiter(void 0, void 0, void 0, function () {
3376
- var mongoose, softDeleteSchema, SoftDeleteModel, testDoc, softDeleted;
3377
- return __generator(this, function (_a) {
3378
- switch (_a.label) {
3379
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("mongoose")); })];
3380
- case 1:
3381
- mongoose = _a.sent();
3382
- softDeleteSchema = new mongoose.Schema({
3383
- deleted: { default: false, type: Boolean },
3384
- name: String,
3385
- });
3386
- try {
3387
- SoftDeleteModel = mongoose.model("SoftDeleteTest");
3388
- }
3389
- catch (_b) {
3390
- SoftDeleteModel = mongoose.model("SoftDeleteTest", softDeleteSchema);
3391
- }
3392
- // Clean up any existing documents
3393
- return [4 /*yield*/, SoftDeleteModel.deleteMany({})];
3394
- case 2:
3395
- // Clean up any existing documents
3396
- _a.sent();
3397
- return [4 /*yield*/, SoftDeleteModel.create({ name: "TestItem" })];
3398
- case 3:
3399
- testDoc = _a.sent();
3400
- app.use("/softdelete", (0, api_1.modelRouter)(SoftDeleteModel, {
1916
+ app.use("/users", (0, api_1.modelRouter)(tests_1.UserModel, {
3401
1917
  allowAnonymous: true,
1918
+ discriminatorKey: "__t",
3402
1919
  permissions: {
3403
- create: [permissions_1.Permissions.IsAny],
3404
- delete: [permissions_1.Permissions.IsAny],
3405
- list: [permissions_1.Permissions.IsAny],
3406
- read: [permissions_1.Permissions.IsAny],
3407
- update: [permissions_1.Permissions.IsAny],
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],
3408
1925
  },
3409
1926
  }));
3410
1927
  server = (0, supertest_1.default)(app);
3411
1928
  return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
3412
- case 4:
3413
- agent = _a.sent();
3414
- // Delete should soft delete (set deleted: true) instead of hard delete
3415
- return [4 /*yield*/, agent.delete("/softdelete/".concat(testDoc._id)).expect(204)];
3416
1929
  case 5:
3417
- // Delete should soft delete (set deleted: true) instead of hard delete
3418
- _a.sent();
3419
- return [4 /*yield*/, SoftDeleteModel.findById(testDoc._id)];
3420
- case 6:
3421
- softDeleted = _a.sent();
3422
- (0, bun_test_1.expect)(softDeleted).not.toBeNull();
3423
- (0, bun_test_1.expect)(softDeleted === null || softDeleted === void 0 ? void 0 : softDeleted.deleted).toBe(true);
3424
- // Clean up
3425
- return [4 /*yield*/, SoftDeleteModel.deleteMany({})];
3426
- case 7:
3427
- // Clean up
3428
- _a.sent();
3429
- return [2 /*return*/];
3430
- }
3431
- });
3432
- }); });
3433
- });
3434
- (0, bun_test_1.describe)("array operation with undefined preUpdate return", function () {
3435
- var admin;
3436
- var apple;
3437
- var agent;
3438
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
3439
- var _a;
3440
- return __generator(this, function (_b) {
3441
- switch (_b.label) {
3442
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
3443
- case 1:
3444
- _a = __read.apply(void 0, [_b.sent(), 1]), admin = _a[0];
3445
- return [4 /*yield*/, tests_1.FoodModel.create({
3446
- calories: 100,
3447
- categories: [
3448
- { name: "Fruit", show: true },
3449
- { name: "Popular", show: false },
3450
- ],
3451
- created: new Date("2021-12-03T00:00:30.000Z"),
3452
- hidden: false,
3453
- name: "Apple",
3454
- ownerId: admin._id,
3455
- tags: ["healthy", "cheap"],
3456
- })];
3457
- case 2:
3458
- apple = _b.sent();
3459
- app = (0, tests_1.getBaseServer)();
3460
- (0, auth_1.setupAuth)(app, tests_1.UserModel);
3461
- (0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
1930
+ agent = _c.sent();
3462
1931
  return [2 /*return*/];
3463
1932
  }
3464
1933
  });
3465
1934
  }); });
3466
- (0, bun_test_1.it)("array operation preUpdate returning undefined for array POST throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
3467
- var res;
1935
+ (0, bun_test_1.it)("gets all users", function () { return __awaiter(void 0, void 0, void 0, function () {
1936
+ var res, data;
3468
1937
  return __generator(this, function (_a) {
3469
1938
  switch (_a.label) {
3470
- case 0:
3471
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3472
- allowAnonymous: true,
3473
- permissions: {
3474
- create: [permissions_1.Permissions.IsAdmin],
3475
- delete: [permissions_1.Permissions.IsAdmin],
3476
- list: [permissions_1.Permissions.IsAdmin],
3477
- read: [permissions_1.Permissions.IsAdmin],
3478
- update: [permissions_1.Permissions.IsAdmin],
3479
- },
3480
- preUpdate: function () { return undefined; },
3481
- }));
3482
- server = (0, supertest_1.default)(app);
3483
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
1939
+ case 0: return [4 /*yield*/, agent.get("/users").expect(200)];
3484
1940
  case 1:
3485
- agent = _a.sent();
3486
- return [4 /*yield*/, agent.post("/food/".concat(apple._id, "/tags")).send({ tags: "organic" }).expect(403)];
3487
- case 2:
3488
1941
  res = _a.sent();
3489
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
3490
- (0, bun_test_1.expect)(res.body.detail).toBe("A body must be returned from preUpdate");
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");
3491
1964
  return [2 /*return*/];
3492
1965
  }
3493
1966
  });
3494
1967
  }); });
3495
- (0, bun_test_1.it)("array operation preUpdate returning null for array PATCH throws error", function () { return __awaiter(void 0, void 0, void 0, function () {
1968
+ (0, bun_test_1.it)("gets a discriminated user", function () { return __awaiter(void 0, void 0, void 0, function () {
3496
1969
  var res;
3497
1970
  return __generator(this, function (_a) {
3498
1971
  switch (_a.label) {
3499
- case 0:
3500
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3501
- allowAnonymous: true,
3502
- permissions: {
3503
- create: [permissions_1.Permissions.IsAdmin],
3504
- delete: [permissions_1.Permissions.IsAdmin],
3505
- list: [permissions_1.Permissions.IsAdmin],
3506
- read: [permissions_1.Permissions.IsAdmin],
3507
- update: [permissions_1.Permissions.IsAdmin],
3508
- },
3509
- preUpdate: function () { return null; },
3510
- }));
3511
- server = (0, supertest_1.default)(app);
3512
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
1972
+ case 0: return [4 /*yield*/, agent.get("/users/".concat(superUser._id)).expect(200)];
3513
1973
  case 1:
3514
- agent = _a.sent();
3515
- return [4 /*yield*/, agent
3516
- .patch("/food/".concat(apple._id, "/tags/healthy"))
3517
- .send({ tags: "unhealthy" })
3518
- .expect(403)];
3519
- case 2:
3520
1974
  res = _a.sent();
3521
- (0, bun_test_1.expect)(res.body.title).toBe("Update not allowed");
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");
3522
1978
  return [2 /*return*/];
3523
1979
  }
3524
1980
  });
3525
1981
  }); });
3526
- (0, bun_test_1.it)("array operation preUpdate error for array DELETE is handled", function () { return __awaiter(void 0, void 0, void 0, function () {
3527
- var res;
1982
+ (0, bun_test_1.it)("updates a discriminated user", function () { return __awaiter(void 0, void 0, void 0, function () {
1983
+ var res, user;
3528
1984
  return __generator(this, function (_a) {
3529
1985
  switch (_a.label) {
3530
- case 0:
3531
- app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
3532
- allowAnonymous: true,
3533
- permissions: {
3534
- create: [permissions_1.Permissions.IsAdmin],
3535
- delete: [permissions_1.Permissions.IsAdmin],
3536
- list: [permissions_1.Permissions.IsAdmin],
3537
- read: [permissions_1.Permissions.IsAdmin],
3538
- update: [permissions_1.Permissions.IsAdmin],
3539
- },
3540
- preUpdate: function () {
3541
- throw new Error("preUpdate error during delete");
3542
- },
3543
- }));
3544
- server = (0, supertest_1.default)(app);
3545
- return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
1986
+ case 0:
1987
+ // Fails without __t.
1988
+ return [4 /*yield*/, agent.patch("/users/".concat(superUser._id)).send({ superTitle: "Batman" }).expect(404)];
3546
1989
  case 1:
3547
- agent = _a.sent();
3548
- return [4 /*yield*/, agent.delete("/food/".concat(apple._id, "/tags/healthy")).expect(400)];
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)];
3549
1996
  case 2:
3550
1997
  res = _a.sent();
3551
- (0, bun_test_1.expect)(res.body.title).toContain("preUpdate hook error");
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");
3552
2005
  return [2 /*return*/];
3553
2006
  }
3554
2007
  });
3555
2008
  }); });
3556
- });
3557
- });
3558
- (0, bun_test_1.describe)("errors module", function () {
3559
- (0, bun_test_1.describe)("APIError", function () {
3560
- (0, bun_test_1.it)("sets default status to 500 when not provided", function () {
3561
- var error = new errors_1.APIError({ title: "Test error" });
3562
- (0, bun_test_1.expect)(error.status).toBe(500);
3563
- });
3564
- (0, bun_test_1.it)("sets status to 500 for invalid status codes below 400", function () {
3565
- var error = new errors_1.APIError({ status: 200, title: "Test error" });
3566
- (0, bun_test_1.expect)(error.status).toBe(500);
3567
- });
3568
- (0, bun_test_1.it)("sets status to 500 for invalid status codes above 599", function () {
3569
- var error = new errors_1.APIError({ status: 600, title: "Test error" });
3570
- (0, bun_test_1.expect)(error.status).toBe(500);
3571
- });
3572
- (0, bun_test_1.it)("includes error stack in message when error is provided", function () {
3573
- var originalError = new Error("Original error");
3574
- var apiError = new errors_1.APIError({
3575
- error: originalError,
3576
- title: "Wrapped error",
3577
- });
3578
- (0, bun_test_1.expect)(apiError.message).toContain("Wrapped error");
3579
- (0, bun_test_1.expect)(originalError.stack).toBeDefined();
3580
- (0, bun_test_1.expect)(apiError.message).toContain(originalError.stack);
3581
- });
3582
- (0, bun_test_1.it)("includes detail in message when provided", function () {
3583
- var error = new errors_1.APIError({
3584
- detail: "More details here",
3585
- title: "Test error",
3586
- });
3587
- (0, bun_test_1.expect)(error.message).toContain("Test error");
3588
- (0, bun_test_1.expect)(error.message).toContain("More details here");
3589
- });
3590
- (0, bun_test_1.it)("sets fields in meta when provided", function () {
3591
- var _a;
3592
- var error = new errors_1.APIError({
3593
- fields: { email: "Invalid email format" },
3594
- title: "Validation error",
3595
- });
3596
- (0, bun_test_1.expect)((_a = error.meta) === null || _a === void 0 ? void 0 : _a.fields).toEqual({ email: "Invalid email format" });
3597
- });
3598
- });
3599
- (0, bun_test_1.describe)("errorsPlugin", function () {
3600
- (0, bun_test_1.it)("adds apiErrors field to schema", function () { return __awaiter(void 0, void 0, void 0, function () {
3601
- var mongoose, errorsPlugin, testSchema;
2009
+ (0, bun_test_1.it)("updates a base user", function () { return __awaiter(void 0, void 0, void 0, function () {
2010
+ var res, user;
3602
2011
  return __generator(this, function (_a) {
3603
2012
  switch (_a.label) {
3604
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("mongoose")); })];
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)];
3605
2017
  case 1:
3606
- mongoose = _a.sent();
3607
- return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./errors")); })];
2018
+ 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)];
3608
2022
  case 2:
3609
- errorsPlugin = (_a.sent()).errorsPlugin;
3610
- testSchema = new mongoose.Schema({ name: String });
3611
- errorsPlugin(testSchema);
3612
- (0, bun_test_1.expect)(testSchema.path("apiErrors")).toBeDefined();
2023
+ user = _a.sent();
2024
+ (0, bun_test_1.expect)(user === null || user === void 0 ? void 0 : user.superTitle).toBeUndefined();
3613
2025
  return [2 /*return*/];
3614
2026
  }
3615
2027
  });
3616
2028
  }); });
3617
- });
3618
- (0, bun_test_1.describe)("isAPIError", function () {
3619
- (0, bun_test_1.it)("returns true for APIError instances", function () {
3620
- var isAPIError = require("./errors").isAPIError;
3621
- var error = new errors_1.APIError({ title: "Test" });
3622
- (0, bun_test_1.expect)(isAPIError(error)).toBe(true);
3623
- });
3624
- (0, bun_test_1.it)("returns false for regular Error instances", function () {
3625
- var isAPIError = require("./errors").isAPIError;
3626
- var error = new Error("Test");
3627
- (0, bun_test_1.expect)(isAPIError(error)).toBe(false);
3628
- });
3629
- });
3630
- (0, bun_test_1.describe)("getDisableExternalErrorTracking", function () {
3631
- (0, bun_test_1.it)("returns undefined for non-objects", function () {
3632
- var getDisableExternalErrorTracking = require("./errors").getDisableExternalErrorTracking;
3633
- (0, bun_test_1.expect)(getDisableExternalErrorTracking(null)).toBeUndefined();
3634
- (0, bun_test_1.expect)(getDisableExternalErrorTracking("string")).toBeUndefined();
3635
- });
3636
- (0, bun_test_1.it)("returns value from APIError", function () {
3637
- var getDisableExternalErrorTracking = require("./errors").getDisableExternalErrorTracking;
3638
- var error = new errors_1.APIError({ disableExternalErrorTracking: true, title: "Test" });
3639
- (0, bun_test_1.expect)(getDisableExternalErrorTracking(error)).toBe(true);
3640
- });
3641
- (0, bun_test_1.it)("returns value from plain object with property", function () {
3642
- var getDisableExternalErrorTracking = require("./errors").getDisableExternalErrorTracking;
3643
- var obj = { disableExternalErrorTracking: true };
3644
- (0, bun_test_1.expect)(getDisableExternalErrorTracking(obj)).toBe(true);
3645
- });
3646
- });
3647
- (0, bun_test_1.describe)("getAPIErrorBody", function () {
3648
- (0, bun_test_1.it)("includes all non-undefined fields", function () {
3649
- var getAPIErrorBody = require("./errors").getAPIErrorBody;
3650
- var error = new errors_1.APIError({
3651
- code: "TEST_CODE",
3652
- detail: "Test detail",
3653
- id: "error-123",
3654
- links: { about: "http://example.com" },
3655
- meta: { extra: "data" },
3656
- source: { parameter: "id" },
3657
- status: 400,
3658
- title: "Test error",
3659
- });
3660
- var body = getAPIErrorBody(error);
3661
- (0, bun_test_1.expect)(body.title).toBe("Test error");
3662
- (0, bun_test_1.expect)(body.status).toBe(400);
3663
- (0, bun_test_1.expect)(body.code).toBe("TEST_CODE");
3664
- (0, bun_test_1.expect)(body.detail).toBe("Test detail");
3665
- (0, bun_test_1.expect)(body.id).toBe("error-123");
3666
- (0, bun_test_1.expect)(body.links).toEqual({ about: "http://example.com" });
3667
- (0, bun_test_1.expect)(body.source).toEqual({ parameter: "id" });
3668
- (0, bun_test_1.expect)(body.meta).toEqual({ extra: "data" });
3669
- });
3670
- });
3671
- (0, bun_test_1.describe)("apiUnauthorizedMiddleware", function () {
3672
- (0, bun_test_1.it)("returns 401 for Unauthorized errors", function () {
3673
- var apiUnauthorizedMiddleware = require("./errors").apiUnauthorizedMiddleware;
3674
- var err = new Error("Unauthorized");
3675
- var res = {
3676
- json: function (data) {
3677
- this.body = data;
3678
- return this;
3679
- },
3680
- send: function () {
3681
- return this;
3682
- },
3683
- status: function (code) {
3684
- this.statusCode = code;
3685
- return this;
3686
- },
3687
- };
3688
- var next = function () { };
3689
- apiUnauthorizedMiddleware(err, {}, res, next);
3690
- (0, bun_test_1.expect)(res.statusCode).toBe(401);
3691
- (0, bun_test_1.expect)(res.body.title).toBe("Unauthorized");
3692
- });
3693
- (0, bun_test_1.it)("calls next for non-Unauthorized errors", function () {
3694
- var apiUnauthorizedMiddleware = require("./errors").apiUnauthorizedMiddleware;
3695
- var err = new Error("Some other error");
3696
- var nextCalled = false;
3697
- var next = function () {
3698
- nextCalled = true;
3699
- };
3700
- apiUnauthorizedMiddleware(err, {}, {}, next);
3701
- (0, bun_test_1.expect)(nextCalled).toBe(true);
3702
- });
3703
- });
3704
- });
3705
- (0, bun_test_1.describe)("permissions module", function () {
3706
- (0, bun_test_1.describe)("OwnerQueryFilter", function () {
3707
- (0, bun_test_1.it)("returns ownerId filter when user is provided", function () {
3708
- var OwnerQueryFilter = require("./permissions").OwnerQueryFilter;
3709
- var user = { id: "user-123" };
3710
- var filter = OwnerQueryFilter(user);
3711
- (0, bun_test_1.expect)(filter).toEqual({ ownerId: "user-123" });
3712
- });
3713
- (0, bun_test_1.it)("returns null when user is undefined", function () {
3714
- var OwnerQueryFilter = require("./permissions").OwnerQueryFilter;
3715
- var filter = OwnerQueryFilter(undefined);
3716
- (0, bun_test_1.expect)(filter).toBeNull();
3717
- });
3718
- });
3719
- (0, bun_test_1.describe)("Permissions.IsAuthenticatedOrReadOnly", function () {
3720
- (0, bun_test_1.it)("returns true for authenticated non-anonymous users", function () {
3721
- var Permissions = require("./permissions").Permissions;
3722
- var user = { id: "user-123", isAnonymous: false };
3723
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("create", user)).toBe(true);
3724
- });
3725
- (0, bun_test_1.it)("returns true for read methods when user is anonymous", function () {
3726
- var Permissions = require("./permissions").Permissions;
3727
- var user = { id: "user-123", isAnonymous: true };
3728
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("list", user)).toBe(true);
3729
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("read", user)).toBe(true);
3730
- });
3731
- (0, bun_test_1.it)("returns false for write methods when user is anonymous", function () {
3732
- var Permissions = require("./permissions").Permissions;
3733
- var user = { id: "user-123", isAnonymous: true };
3734
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("create", user)).toBe(false);
3735
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("update", user)).toBe(false);
3736
- (0, bun_test_1.expect)(Permissions.IsAuthenticatedOrReadOnly("delete", user)).toBe(false);
3737
- });
3738
- });
3739
- (0, bun_test_1.describe)("Permissions.IsOwnerOrReadOnly", function () {
3740
- (0, bun_test_1.it)("returns true when no object is provided", function () {
3741
- var Permissions = require("./permissions").Permissions;
3742
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("update", { id: "user-123" }, undefined)).toBe(true);
3743
- });
3744
- (0, bun_test_1.it)("returns true for admin users", function () {
3745
- var Permissions = require("./permissions").Permissions;
3746
- var user = { admin: true, id: "admin-123" };
3747
- var obj = { ownerId: "other-user" };
3748
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("update", user, obj)).toBe(true);
3749
- });
3750
- (0, bun_test_1.it)("returns true when user is owner", function () {
3751
- var Permissions = require("./permissions").Permissions;
3752
- var user = { id: "user-123" };
3753
- var obj = { ownerId: "user-123" };
3754
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("update", user, obj)).toBe(true);
3755
- });
3756
- (0, bun_test_1.it)("returns true for read methods when not owner", function () {
3757
- var Permissions = require("./permissions").Permissions;
3758
- var user = { id: "user-123" };
3759
- var obj = { ownerId: "other-user" };
3760
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("list", user, obj)).toBe(true);
3761
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("read", user, obj)).toBe(true);
3762
- });
3763
- (0, bun_test_1.it)("returns false for write methods when not owner", function () {
3764
- var Permissions = require("./permissions").Permissions;
3765
- var user = { id: "user-123" };
3766
- var obj = { ownerId: "other-user" };
3767
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("update", user, obj)).toBe(false);
3768
- (0, bun_test_1.expect)(Permissions.IsOwnerOrReadOnly("delete", user, obj)).toBe(false);
3769
- });
3770
- });
3771
- });
3772
- (0, bun_test_1.describe)("utils module", function () {
3773
- (0, bun_test_1.describe)("isValidObjectId", function () {
3774
- (0, bun_test_1.it)("returns true for valid ObjectId strings", function () {
3775
- var isValidObjectId = require("./utils").isValidObjectId;
3776
- (0, bun_test_1.expect)(isValidObjectId("507f1f77bcf86cd799439011")).toBe(true);
3777
- });
3778
- (0, bun_test_1.it)("returns false for invalid ObjectId strings", function () {
3779
- var isValidObjectId = require("./utils").isValidObjectId;
3780
- (0, bun_test_1.expect)(isValidObjectId("invalid-id")).toBe(false);
3781
- (0, bun_test_1.expect)(isValidObjectId("12345")).toBe(false);
3782
- (0, bun_test_1.expect)(isValidObjectId("")).toBe(false);
3783
- });
3784
- (0, bun_test_1.it)("returns false for 12-character strings that are not valid ObjectIds", function () {
3785
- var isValidObjectId = require("./utils").isValidObjectId;
3786
- // mongoose's native isValid returns true for any 12-char string
3787
- // but our implementation should return false since toString won't match
3788
- (0, bun_test_1.expect)(isValidObjectId("123456789012")).toBe(false);
3789
- });
3790
- });
3791
- (0, bun_test_1.describe)("timeout", function () {
3792
- (0, bun_test_1.it)("resolves after specified time", function () { return __awaiter(void 0, void 0, void 0, function () {
3793
- var timeout, start, elapsed;
2029
+ (0, bun_test_1.it)("cannot update discriminator key", function () { return __awaiter(void 0, void 0, void 0, function () {
3794
2030
  return __generator(this, function (_a) {
3795
2031
  switch (_a.label) {
3796
- case 0:
3797
- timeout = require("./utils").timeout;
3798
- start = Date.now();
3799
- return [4 /*yield*/, timeout(50)];
2032
+ case 0: return [4 /*yield*/, agent
2033
+ .patch("/users/".concat(notAdmin._id))
2034
+ .send({ __t: "Staff", superTitle: "Batman" })
2035
+ .expect(404)];
3800
2036
  case 1:
3801
2037
  _a.sent();
3802
- elapsed = Date.now() - start;
3803
- (0, bun_test_1.expect)(elapsed).toBeGreaterThanOrEqual(40);
3804
- return [2 /*return*/];
3805
- }
3806
- });
3807
- }); });
3808
- });
3809
- // Note: Comprehensive checkModelsStrict tests are in utils.test.ts with mocked mongoose
3810
- });
3811
- (0, bun_test_1.describe)("populate module", function () {
3812
- (0, bun_test_1.describe)("unpopulate", function () {
3813
- (0, bun_test_1.it)("throws error when path is empty", function () { return __awaiter(void 0, void 0, void 0, function () {
3814
- var unpopulate, doc;
3815
- return __generator(this, function (_a) {
3816
- switch (_a.label) {
3817
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3818
- case 1:
3819
- unpopulate = (_a.sent()).unpopulate;
3820
- doc = { name: "test" };
3821
- (0, bun_test_1.expect)(function () { return unpopulate(doc, ""); }).toThrow("path is required");
3822
- return [2 /*return*/];
3823
- }
3824
- });
3825
- }); });
3826
- (0, bun_test_1.it)("unpopulates single populated field", function () { return __awaiter(void 0, void 0, void 0, function () {
3827
- var unpopulate, doc, result;
3828
- return __generator(this, function (_a) {
3829
- switch (_a.label) {
3830
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3831
- case 1:
3832
- unpopulate = (_a.sent()).unpopulate;
3833
- doc = {
3834
- name: "test",
3835
- ownerId: { _id: "owner-123", email: "owner@test.com" },
3836
- };
3837
- result = unpopulate(doc, "ownerId");
3838
- (0, bun_test_1.expect)(result.ownerId).toBe("owner-123");
3839
- return [2 /*return*/];
3840
- }
3841
- });
3842
- }); });
3843
- (0, bun_test_1.it)("unpopulates array of populated fields", function () { return __awaiter(void 0, void 0, void 0, function () {
3844
- var unpopulate, doc, result;
3845
- return __generator(this, function (_a) {
3846
- switch (_a.label) {
3847
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3848
- case 1:
3849
- unpopulate = (_a.sent()).unpopulate;
3850
- doc = {
3851
- items: [{ _id: "item-1", name: "Item 1" }, { _id: "item-2", name: "Item 2" }, "item-3"],
3852
- name: "test",
3853
- };
3854
- result = unpopulate(doc, "items");
3855
- (0, bun_test_1.expect)(result.items).toEqual(["item-1", "item-2", "item-3"]);
3856
- return [2 /*return*/];
3857
- }
3858
- });
3859
- }); });
3860
- (0, bun_test_1.it)("handles nested paths", function () { return __awaiter(void 0, void 0, void 0, function () {
3861
- var unpopulate, doc, result;
3862
- return __generator(this, function (_a) {
3863
- switch (_a.label) {
3864
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3865
- case 1:
3866
- unpopulate = (_a.sent()).unpopulate;
3867
- doc = {
3868
- name: "test",
3869
- nested: {
3870
- items: [
3871
- { _id: "item-1", name: "Item 1" },
3872
- { _id: "item-2", name: "Item 2" },
3873
- ],
3874
- },
3875
- };
3876
- result = unpopulate(doc, "nested.items");
3877
- (0, bun_test_1.expect)(result.nested.items).toEqual(["item-1", "item-2"]);
3878
- return [2 /*return*/];
3879
- }
3880
- });
3881
- }); });
3882
- (0, bun_test_1.it)("returns original doc when path does not exist", function () { return __awaiter(void 0, void 0, void 0, function () {
3883
- var unpopulate, doc, result;
3884
- return __generator(this, function (_a) {
3885
- switch (_a.label) {
3886
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3887
- case 1:
3888
- unpopulate = (_a.sent()).unpopulate;
3889
- doc = { name: "test" };
3890
- result = unpopulate(doc, "nonexistent");
3891
- (0, bun_test_1.expect)(result).toEqual(doc);
3892
- return [2 /*return*/];
3893
- }
3894
- });
3895
- }); });
3896
- (0, bun_test_1.it)("handles nested array paths", function () { return __awaiter(void 0, void 0, void 0, function () {
3897
- var unpopulate, doc, result;
3898
- return __generator(this, function (_a) {
3899
- switch (_a.label) {
3900
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./populate")); })];
3901
- case 1:
3902
- unpopulate = (_a.sent()).unpopulate;
3903
- doc = {
3904
- containers: [
3905
- { items: [{ _id: "item-1" }, { _id: "item-2" }] },
3906
- { items: [{ _id: "item-3" }, { _id: "item-4" }] },
3907
- ],
3908
- name: "test",
3909
- };
3910
- result = unpopulate(doc, "containers.items");
3911
- (0, bun_test_1.expect)(result.containers[0].items).toEqual(["item-1", "item-2"]);
3912
- (0, bun_test_1.expect)(result.containers[1].items).toEqual(["item-3", "item-4"]);
3913
- return [2 /*return*/];
3914
- }
3915
- });
3916
- }); });
3917
- });
3918
- });
3919
- (0, bun_test_1.describe)("auth module edge cases", function () {
3920
- (0, bun_test_1.describe)("generateTokens", function () {
3921
- (0, bun_test_1.it)("returns null tokens when user is missing", function () { return __awaiter(void 0, void 0, void 0, function () {
3922
- var generateTokens, result;
3923
- return __generator(this, function (_a) {
3924
- switch (_a.label) {
3925
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./auth")); })];
3926
- case 1:
3927
- generateTokens = (_a.sent()).generateTokens;
3928
- return [4 /*yield*/, generateTokens(null)];
2038
+ return [4 /*yield*/, agent
2039
+ .patch("/users/".concat(staffUser._id))
2040
+ .send({ __t: "SuperUser", superTitle: "Batman" })
2041
+ .expect(404)];
3929
2042
  case 2:
3930
- result = _a.sent();
3931
- (0, bun_test_1.expect)(result.token).toBeNull();
3932
- (0, bun_test_1.expect)(result.refreshToken).toBeNull();
2043
+ _a.sent();
3933
2044
  return [2 /*return*/];
3934
2045
  }
3935
2046
  });
3936
2047
  }); });
3937
- (0, bun_test_1.it)("returns null tokens when user has no _id", function () { return __awaiter(void 0, void 0, void 0, function () {
3938
- var generateTokens, result;
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;
3939
2050
  return __generator(this, function (_a) {
3940
2051
  switch (_a.label) {
3941
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./auth")); })];
2052
+ case 0: return [4 /*yield*/, agent
2053
+ .patch("/users/".concat(superUser._id))
2054
+ .send({ __t: "SuperUser", department: "Journalism" })
2055
+ .expect(200)];
3942
2056
  case 1:
3943
- generateTokens = (_a.sent()).generateTokens;
3944
- return [4 /*yield*/, generateTokens({ email: "test@test.com" })];
2057
+ res = _a.sent();
2058
+ (0, bun_test_1.expect)(res.body.data.department).toBeUndefined();
2059
+ return [4 /*yield*/, tests_1.SuperUserModel.findById(superUser._id)];
3945
2060
  case 2:
3946
- result = _a.sent();
3947
- (0, bun_test_1.expect)(result.token).toBeNull();
3948
- (0, bun_test_1.expect)(result.refreshToken).toBeNull();
2061
+ user = _a.sent();
2062
+ (0, bun_test_1.expect)(user === null || user === void 0 ? void 0 : user.department).toBeUndefined();
3949
2063
  return [2 /*return*/];
3950
2064
  }
3951
2065
  });
3952
2066
  }); });
3953
- (0, bun_test_1.it)("includes custom payload from generateJWTPayload option", function () { return __awaiter(void 0, void 0, void 0, function () {
3954
- var generateTokens, jwt, user, result, decoded;
2067
+ (0, bun_test_1.it)("creates a discriminated user", function () { return __awaiter(void 0, void 0, void 0, function () {
2068
+ var res, user;
3955
2069
  return __generator(this, function (_a) {
3956
2070
  switch (_a.label) {
3957
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./auth")); })];
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)];
3958
2080
  case 1:
3959
- generateTokens = (_a.sent()).generateTokens;
3960
- return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("jsonwebtoken")); })];
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)];
3961
2088
  case 2:
3962
- jwt = _a.sent();
3963
- user = { _id: "user-123" };
3964
- return [4 /*yield*/, generateTokens(user, {
3965
- generateJWTPayload: function (u) { return ({ customField: "customValue", userId: u._id }); },
3966
- })];
3967
- case 3:
3968
- result = _a.sent();
3969
- (0, bun_test_1.expect)(result.token).toBeDefined();
3970
- decoded = jwt.decode(result.token);
3971
- (0, bun_test_1.expect)(decoded.customField).toBe("customValue");
3972
- (0, bun_test_1.expect)(decoded.id).toBe("user-123");
2089
+ user = _a.sent();
2090
+ (0, bun_test_1.expect)(user === null || user === void 0 ? void 0 : user.superTitle).toBe("Batman");
3973
2091
  return [2 /*return*/];
3974
2092
  }
3975
2093
  });
3976
2094
  }); });
3977
- (0, bun_test_1.it)("uses custom token expiration from generateTokenExpiration option", function () { return __awaiter(void 0, void 0, void 0, function () {
3978
- var generateTokens, jwt, user, result, decoded, expectedExp;
2095
+ (0, bun_test_1.it)("deletes a discriminated user", function () { return __awaiter(void 0, void 0, void 0, function () {
2096
+ var user;
3979
2097
  return __generator(this, function (_a) {
3980
2098
  switch (_a.label) {
3981
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./auth")); })];
2099
+ case 0:
2100
+ // Fails without __t.
2101
+ return [4 /*yield*/, agent.delete("/users/".concat(superUser._id)).expect(404)];
3982
2102
  case 1:
3983
- generateTokens = (_a.sent()).generateTokens;
3984
- return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("jsonwebtoken")); })];
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)];
3985
2111
  case 2:
3986
- jwt = _a.sent();
3987
- user = { _id: "user-123" };
3988
- return [4 /*yield*/, generateTokens(user, {
3989
- generateTokenExpiration: function () { return "1h"; },
3990
- })];
2112
+ _a.sent();
2113
+ return [4 /*yield*/, tests_1.SuperUserModel.findById(superUser._id)];
3991
2114
  case 3:
3992
- result = _a.sent();
3993
- (0, bun_test_1.expect)(result.token).toBeDefined();
3994
- decoded = jwt.decode(result.token);
3995
- expectedExp = Math.floor(Date.now() / 1000) + 3600;
3996
- (0, bun_test_1.expect)(decoded.exp).toBeGreaterThan(expectedExp - 5);
3997
- (0, bun_test_1.expect)(decoded.exp).toBeLessThan(expectedExp + 5);
2115
+ user = _a.sent();
2116
+ (0, bun_test_1.expect)(user).toBeNull();
3998
2117
  return [2 /*return*/];
3999
2118
  }
4000
2119
  });
4001
2120
  }); });
4002
- (0, bun_test_1.it)("uses custom refresh token expiration from generateRefreshTokenExpiration option", function () { return __awaiter(void 0, void 0, void 0, function () {
4003
- var generateTokens, jwt, user, result, decoded, expectedExp;
2121
+ (0, bun_test_1.it)("deletes a base user", function () { return __awaiter(void 0, void 0, void 0, function () {
2122
+ var user;
4004
2123
  return __generator(this, function (_a) {
4005
2124
  switch (_a.label) {
4006
- case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("./auth")); })];
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)];
4007
2128
  case 1:
4008
- generateTokens = (_a.sent()).generateTokens;
4009
- return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("jsonwebtoken")); })];
2129
+ // Fails for base user with __t
2130
+ _a.sent();
2131
+ return [4 /*yield*/, agent.delete("/users/".concat(notAdmin._id)).expect(204)];
4010
2132
  case 2:
4011
- jwt = _a.sent();
4012
- user = { _id: "user-123" };
4013
- return [4 /*yield*/, generateTokens(user, {
4014
- generateRefreshTokenExpiration: function () { return "7d"; },
4015
- })];
2133
+ _a.sent();
2134
+ return [4 /*yield*/, tests_1.SuperUserModel.findById(notAdmin._id)];
4016
2135
  case 3:
4017
- result = _a.sent();
4018
- (0, bun_test_1.expect)(result.refreshToken).toBeDefined();
4019
- decoded = jwt.decode(result.refreshToken);
4020
- expectedExp = Math.floor(Date.now() / 1000) + 7 * 24 * 3600;
4021
- (0, bun_test_1.expect)(decoded.exp).toBeGreaterThan(expectedExp - 10);
4022
- (0, bun_test_1.expect)(decoded.exp).toBeLessThan(expectedExp + 10);
2136
+ user = _a.sent();
2137
+ (0, bun_test_1.expect)(user).toBeNull();
4023
2138
  return [2 /*return*/];
4024
2139
  }
4025
2140
  });