@terreno/api 0.21.0 → 0.22.1
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/bunfig.toml +1 -1
- package/dist/auth.test.js +408 -33
- package/dist/models/consentForm.js +2 -1
- package/dist/models/consentResponse.js +2 -1
- package/dist/models/versionConfig.js +2 -1
- package/dist/openApiBuilder.d.ts +18 -0
- package/dist/openApiBuilder.js +21 -0
- package/dist/openApiBuilder.test.js +16 -0
- package/dist/permissions.test.js +10 -43
- package/dist/populate.test.js +10 -42
- package/dist/syncConsents.test.js +2 -2
- package/dist/tests/bunSetup.js +33 -283
- package/dist/tests/createTestData.d.ts +9 -0
- package/dist/tests/createTestData.js +272 -0
- package/dist/tests/models.d.ts +71 -0
- package/dist/tests/models.js +134 -0
- package/dist/tests/mongoTestSetup.d.ts +7 -0
- package/dist/tests/mongoTestSetup.js +150 -0
- package/dist/tests/testEnv.d.ts +0 -0
- package/dist/tests/testEnv.js +6 -0
- package/dist/tests/testHelper.d.ts +22 -0
- package/dist/tests/testHelper.js +115 -0
- package/dist/tests/types.d.ts +29 -0
- package/dist/tests/types.js +2 -0
- package/dist/tests.d.ts +10 -78
- package/dist/tests.js +24 -264
- package/dist/transformers.test.js +14 -50
- package/package.json +18 -4
- package/src/__snapshots__/openApiBuilder.test.ts.snap +1 -0
- package/src/auth.test.ts +277 -29
- package/src/models/consentForm.ts +3 -4
- package/src/models/consentResponse.ts +6 -4
- package/src/models/versionConfig.ts +3 -4
- package/src/openApiBuilder.test.ts +9 -0
- package/src/openApiBuilder.ts +24 -0
- package/src/permissions.test.ts +8 -23
- package/src/populate.test.ts +7 -22
- package/src/syncConsents.test.ts +1 -1
- package/src/tests/bunSetup.ts +22 -249
- package/src/tests/createTestData.ts +176 -0
- package/src/tests/models.ts +164 -0
- package/src/tests/mongoTestSetup.ts +69 -0
- package/src/tests/testEnv.ts +4 -0
- package/src/tests/testHelper.ts +57 -0
- package/src/tests/types.ts +35 -0
- package/src/tests.ts +40 -244
- package/src/transformers.test.ts +11 -30
- package/tsconfig.typedoc.json +4 -0
- package/dist/tests/index.d.ts +0 -1
- package/dist/tests/index.js +0 -17
- package/src/tests/index.ts +0 -1
package/src/permissions.test.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
FoodModel,
|
|
14
14
|
getBaseServer,
|
|
15
15
|
RequiredModel,
|
|
16
|
-
|
|
16
|
+
setupTestData,
|
|
17
17
|
UserModel,
|
|
18
18
|
} from "./tests";
|
|
19
19
|
|
|
@@ -24,22 +24,7 @@ describe("permissions", () => {
|
|
|
24
24
|
beforeEach(async () => {
|
|
25
25
|
process.env.REFRESH_TOKEN_SECRET = "testsecret1234";
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
await Promise.all([
|
|
30
|
-
FoodModel.create({
|
|
31
|
-
calories: 1,
|
|
32
|
-
created: new Date(),
|
|
33
|
-
name: "Spinach",
|
|
34
|
-
ownerId: notAdmin._id,
|
|
35
|
-
}),
|
|
36
|
-
FoodModel.create({
|
|
37
|
-
calories: 100,
|
|
38
|
-
created: Date.now() - 10,
|
|
39
|
-
name: "Apple",
|
|
40
|
-
ownerId: admin._id,
|
|
41
|
-
}),
|
|
42
|
-
]);
|
|
27
|
+
await setupTestData();
|
|
43
28
|
app = getBaseServer();
|
|
44
29
|
setupAuth(app, UserModel as any);
|
|
45
30
|
addAuthRoutes(app, UserModel as any);
|
|
@@ -74,12 +59,12 @@ describe("permissions", () => {
|
|
|
74
59
|
describe("anonymous food", () => {
|
|
75
60
|
it("list", async () => {
|
|
76
61
|
const res = await server.get("/food").expect(200);
|
|
77
|
-
expect(res.body.data).toHaveLength(
|
|
62
|
+
expect(res.body.data).toHaveLength(4);
|
|
78
63
|
});
|
|
79
64
|
|
|
80
65
|
it("get", async () => {
|
|
81
66
|
const res = await server.get("/food").expect(200);
|
|
82
|
-
expect(res.body.data).toHaveLength(
|
|
67
|
+
expect(res.body.data).toHaveLength(4);
|
|
83
68
|
const res2 = await server.get(`/food/${res.body.data[0]._id}`).expect(200);
|
|
84
69
|
expect(res.body.data[0]._id).toBe(res2.body.data._id);
|
|
85
70
|
});
|
|
@@ -116,12 +101,12 @@ describe("permissions", () => {
|
|
|
116
101
|
|
|
117
102
|
it("list", async () => {
|
|
118
103
|
const res = await agent.get("/food").expect(200);
|
|
119
|
-
expect(res.body.data).toHaveLength(
|
|
104
|
+
expect(res.body.data).toHaveLength(4);
|
|
120
105
|
});
|
|
121
106
|
|
|
122
107
|
it("get", async () => {
|
|
123
108
|
const res = await agent.get("/food").expect(200);
|
|
124
|
-
expect(res.body.data).toHaveLength(
|
|
109
|
+
expect(res.body.data).toHaveLength(4);
|
|
125
110
|
const res2 = await server.get(`/food/${res.body.data[0]._id}`).expect(200);
|
|
126
111
|
expect(res.body.data[0]._id).toBe(res2.body.data._id);
|
|
127
112
|
});
|
|
@@ -175,12 +160,12 @@ describe("permissions", () => {
|
|
|
175
160
|
|
|
176
161
|
it("list", async () => {
|
|
177
162
|
const res = await agent.get("/food");
|
|
178
|
-
expect(res.body.data).toHaveLength(
|
|
163
|
+
expect(res.body.data).toHaveLength(4);
|
|
179
164
|
});
|
|
180
165
|
|
|
181
166
|
it("get", async () => {
|
|
182
167
|
const res = await agent.get("/food");
|
|
183
|
-
expect(res.body.data).toHaveLength(
|
|
168
|
+
expect(res.body.data).toHaveLength(4);
|
|
184
169
|
const res2 = await agent.get(`/food/${res.body.data[0]._id}`);
|
|
185
170
|
expect(res.body.data[0]._id).toBe(res2.body.data._id);
|
|
186
171
|
});
|
package/src/populate.test.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {beforeEach, describe, expect, it} from "bun:test";
|
|
|
3
3
|
import mongoose, {type Document, type HydratedDocument, Schema} from "mongoose";
|
|
4
4
|
|
|
5
5
|
import {fixMixedFields, getOpenApiSpecForModel, unpopulate} from "./populate";
|
|
6
|
-
import {FoodModel,
|
|
6
|
+
import {FoodModel, setupTestData, type User, UserModel} from "./tests";
|
|
7
7
|
|
|
8
8
|
describe("populate functions", () => {
|
|
9
9
|
let admin: HydratedDocument<User>;
|
|
@@ -13,32 +13,17 @@ describe("populate functions", () => {
|
|
|
13
13
|
let spinach: any;
|
|
14
14
|
|
|
15
15
|
beforeEach(async () => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
calories: 1,
|
|
21
|
-
created: new Date("2021-12-03T00:00:20.000Z"),
|
|
22
|
-
eatenBy: [admin._id],
|
|
23
|
-
hidden: false,
|
|
24
|
-
likesIds: [
|
|
25
|
-
{likes: true, userId: admin._id},
|
|
26
|
-
{likes: false, userId: notAdmin._id},
|
|
27
|
-
],
|
|
28
|
-
name: "Spinach",
|
|
29
|
-
ownerId: admin._id,
|
|
30
|
-
source: {
|
|
31
|
-
name: "Brand",
|
|
32
|
-
},
|
|
33
|
-
}),
|
|
34
|
-
]);
|
|
16
|
+
const testData = await setupTestData();
|
|
17
|
+
admin = testData.users.admin;
|
|
18
|
+
notAdmin = testData.users.notAdmin;
|
|
19
|
+
spinach = testData.foods.spinach;
|
|
35
20
|
});
|
|
36
21
|
|
|
37
22
|
it("unpopulate", async () => {
|
|
38
23
|
let populated = await spinach.populate("ownerId");
|
|
39
24
|
populated = await populated.populate("eatenBy");
|
|
40
25
|
populated = await populated.populate("likesIds.userId");
|
|
41
|
-
expect(populated.ownerId.name).toBe("Admin");
|
|
26
|
+
expect(populated.ownerId.name).toBe("Not Admin");
|
|
42
27
|
expect(populated.eatenBy[0].id).toBe(admin.id);
|
|
43
28
|
expect(populated.eatenBy[0].name).toBe("Admin");
|
|
44
29
|
expect(populated.likesIds[0].userId.id).toBe(admin.id);
|
|
@@ -49,7 +34,7 @@ describe("populate functions", () => {
|
|
|
49
34
|
// noExplicitAny: unpopulate returns Document<T> which doesn't expose model properties; would require refactoring the return type
|
|
50
35
|
let unpopulated: any = unpopulate(populated, "ownerId");
|
|
51
36
|
expect(spinach.ownerId.name).toBeUndefined();
|
|
52
|
-
expect(unpopulated.ownerId.toString()).toBe(
|
|
37
|
+
expect(unpopulated.ownerId.toString()).toBe(notAdmin.id);
|
|
53
38
|
// Ensure nothing else was touched.
|
|
54
39
|
expect(populated.likesIds[0].userId.id).toBe(admin.id);
|
|
55
40
|
expect(populated.likesIds[0].userId.name).toBe("Admin");
|
package/src/syncConsents.test.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {afterEach, beforeEach, describe, expect, it} from "bun:test";
|
|
|
2
2
|
import {ConsentForm} from "./models/consentForm";
|
|
3
3
|
import type {ConsentFormDefinition} from "./syncConsents";
|
|
4
4
|
import {syncConsents} from "./syncConsents";
|
|
5
|
-
import {setupDb} from "./tests";
|
|
5
|
+
import {setupDb} from "./tests/testHelper";
|
|
6
6
|
|
|
7
7
|
const baseDef: ConsentFormDefinition = {
|
|
8
8
|
content: {en: "# Terms\nPlease agree."},
|
package/src/tests/bunSetup.ts
CHANGED
|
@@ -1,254 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
import {afterAll, afterEach, beforeAll, beforeEach, mock} from "bun:test";
|
|
3
|
-
import {Writable} from "node:stream";
|
|
4
|
-
import mongoose from "mongoose";
|
|
5
|
-
import winston from "winston";
|
|
1
|
+
import {registerBackendPreload, registerSimpleMongoPreload} from "@terreno/test";
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
import {logger, winstonLogger} from "../logger";
|
|
3
|
+
const useFixtureCache = process.env.TERRENO_TEST_USE_FIXTURE_CACHE === "true";
|
|
9
4
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
const connectUri = uri ?? defaultLocalMongoUri;
|
|
27
|
-
await mongoose.connect(connectUri).catch(logger.catch);
|
|
5
|
+
if (useFixtureCache) {
|
|
6
|
+
registerBackendPreload({
|
|
7
|
+
connectMongoInBeforeAll: true,
|
|
8
|
+
loadTestDataFromCache: async () => {
|
|
9
|
+
const {loadTestDataFromCache} = await import("./mongoTestSetup");
|
|
10
|
+
await loadTestDataFromCache();
|
|
11
|
+
},
|
|
12
|
+
mongo: {
|
|
13
|
+
baseDatabaseName: "terrenoTest_base",
|
|
14
|
+
useReplSet: true,
|
|
15
|
+
},
|
|
16
|
+
testEnv: {
|
|
17
|
+
tokenIssuer: "terreno-api.test",
|
|
18
|
+
},
|
|
19
|
+
useTransactions: true,
|
|
28
20
|
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
await mongoose.connection.close();
|
|
35
|
-
if (memoryMongo) {
|
|
36
|
-
await memoryMongo.stop();
|
|
37
|
-
}
|
|
21
|
+
} else {
|
|
22
|
+
registerSimpleMongoPreload({
|
|
23
|
+
testEnv: {
|
|
24
|
+
tokenIssuer: "terreno-api.test",
|
|
25
|
+
},
|
|
38
26
|
});
|
|
39
27
|
}
|
|
40
|
-
|
|
41
|
-
let logs: string[] = [];
|
|
42
|
-
|
|
43
|
-
const SHOW_ALL_LOGS = process.env.SHOW_ALL_TEST_LOGS === "true";
|
|
44
|
-
|
|
45
|
-
// Create a custom stream that captures logs
|
|
46
|
-
const logStream = new Writable({
|
|
47
|
-
write(chunk: any, _encoding: any, callback: any) {
|
|
48
|
-
logs.push(chunk.toString());
|
|
49
|
-
if (SHOW_ALL_LOGS) {
|
|
50
|
-
process.stdout.write(chunk);
|
|
51
|
-
}
|
|
52
|
-
callback();
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// Silence both winston loggers by replacing all transports with our capturing stream
|
|
57
|
-
const silentTransport = new winston.transports.Stream({
|
|
58
|
-
format: winston.format.simple(),
|
|
59
|
-
stream: logStream,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Clear and silence the default winston logger
|
|
63
|
-
winston.clear();
|
|
64
|
-
winston.add(silentTransport);
|
|
65
|
-
|
|
66
|
-
// Clear and silence the custom winstonLogger
|
|
67
|
-
winstonLogger.clear();
|
|
68
|
-
winstonLogger.add(silentTransport);
|
|
69
|
-
|
|
70
|
-
// Capture and silence console methods
|
|
71
|
-
const originalConsole = {
|
|
72
|
-
debug: console.debug,
|
|
73
|
-
error: console.error,
|
|
74
|
-
info: console.info,
|
|
75
|
-
// biome-ignore lint/suspicious/noConsole: We keep the original reference.
|
|
76
|
-
log: console.log,
|
|
77
|
-
warn: console.warn,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const captureConsoleMethod = (method: keyof typeof originalConsole): void => {
|
|
81
|
-
(console as any)[method] = (...args: any[]) => {
|
|
82
|
-
const logMessage = `[console.${method}] ${args.map((arg) => (typeof arg === "object" ? JSON.stringify(arg) : String(arg))).join(" ")}`;
|
|
83
|
-
logs.push(logMessage);
|
|
84
|
-
if (SHOW_ALL_LOGS) {
|
|
85
|
-
originalConsole[method](...args);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
captureConsoleMethod("log");
|
|
91
|
-
captureConsoleMethod("info");
|
|
92
|
-
captureConsoleMethod("warn");
|
|
93
|
-
captureConsoleMethod("error");
|
|
94
|
-
captureConsoleMethod("debug");
|
|
95
|
-
|
|
96
|
-
// Setup before each test
|
|
97
|
-
beforeEach(() => {
|
|
98
|
-
process.env.TOKEN_SECRET = "secret";
|
|
99
|
-
process.env.TOKEN_ISSUER = "terreno-api.test";
|
|
100
|
-
process.env.SESSION_SECRET = "sessionSecret";
|
|
101
|
-
process.env.REFRESH_TOKEN_SECRET = "refreshTokenSecret";
|
|
102
|
-
setupEnvironment();
|
|
103
|
-
// Re-silence loggers after setupEnvironment which may reconfigure them
|
|
104
|
-
winston.clear();
|
|
105
|
-
winston.add(silentTransport);
|
|
106
|
-
winstonLogger.clear();
|
|
107
|
-
winstonLogger.add(silentTransport);
|
|
108
|
-
logs = [];
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// Clear logs after each test
|
|
112
|
-
afterEach(() => {
|
|
113
|
-
logs = [];
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Mock @sentry/bun module
|
|
117
|
-
mock.module("@sentry/bun", () => {
|
|
118
|
-
const mockFn = (): ReturnType<typeof mock> => mock(() => {});
|
|
119
|
-
|
|
120
|
-
// Mock Scope
|
|
121
|
-
const mockScope = {
|
|
122
|
-
addBreadcrumb: mockFn(),
|
|
123
|
-
clear: mockFn(),
|
|
124
|
-
getSpan: mockFn(),
|
|
125
|
-
setContext: mockFn(),
|
|
126
|
-
setFingerprint: mockFn(),
|
|
127
|
-
setLevel: mockFn(),
|
|
128
|
-
setSpan: mockFn(),
|
|
129
|
-
setTag: mockFn(),
|
|
130
|
-
setTags: mockFn(),
|
|
131
|
-
setTransactionName: mockFn(),
|
|
132
|
-
setUser: mockFn(),
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
// Mock Hub
|
|
136
|
-
const mockClient = {
|
|
137
|
-
captureException: mockFn(),
|
|
138
|
-
captureMessage: mockFn(),
|
|
139
|
-
close: mock(() => Promise.resolve(true)),
|
|
140
|
-
flush: mock(() => Promise.resolve(true)),
|
|
141
|
-
getOptions: mock(() => ({})),
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const mockHub = {
|
|
145
|
-
addBreadcrumb: mockFn(),
|
|
146
|
-
captureException: mockFn(),
|
|
147
|
-
captureMessage: mockFn(),
|
|
148
|
-
configureScope: mockFn(),
|
|
149
|
-
getClient: mock(() => mockClient),
|
|
150
|
-
getScope: mock(() => mockScope),
|
|
151
|
-
popScope: mockFn(),
|
|
152
|
-
pushScope: mockFn(),
|
|
153
|
-
setContext: mockFn(),
|
|
154
|
-
setTag: mockFn(),
|
|
155
|
-
setTags: mockFn(),
|
|
156
|
-
setUser: mockFn(),
|
|
157
|
-
withScope: mockFn(),
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
const mockSpan: any = {
|
|
161
|
-
finish: mockFn(),
|
|
162
|
-
setData: mockFn(),
|
|
163
|
-
setStatus: mockFn(),
|
|
164
|
-
setTag: mockFn(),
|
|
165
|
-
startChild: mockFn(),
|
|
166
|
-
toTraceparent: mock(() => "mock-trace-parent"),
|
|
167
|
-
};
|
|
168
|
-
mockSpan.startChild = mock(() => mockSpan);
|
|
169
|
-
|
|
170
|
-
const mockTransaction = {
|
|
171
|
-
finish: mockFn(),
|
|
172
|
-
setData: mockFn(),
|
|
173
|
-
setName: mockFn(),
|
|
174
|
-
setStatus: mockFn(),
|
|
175
|
-
setTag: mockFn(),
|
|
176
|
-
startChild: mock(() => mockSpan),
|
|
177
|
-
toTraceparent: mock(() => "mock-trace-parent"),
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
addBreadcrumb: mockFn(),
|
|
182
|
-
captureException: mockFn(),
|
|
183
|
-
captureMessage: mockFn(),
|
|
184
|
-
clearScope: mockFn(),
|
|
185
|
-
close: mock(() => Promise.resolve(true)),
|
|
186
|
-
configureScope: mockFn(),
|
|
187
|
-
default: {
|
|
188
|
-
addBreadcrumb: mockFn(),
|
|
189
|
-
captureException: mockFn(),
|
|
190
|
-
captureMessage: mockFn(),
|
|
191
|
-
clearScope: mockFn(),
|
|
192
|
-
close: mock(() => Promise.resolve(true)),
|
|
193
|
-
configureScope: mockFn(),
|
|
194
|
-
flush: mock(() => Promise.resolve(true)),
|
|
195
|
-
getClient: mock(() => mockClient),
|
|
196
|
-
getCurrentHub: mock(() => mockHub),
|
|
197
|
-
getCurrentScope: mock(() => mockScope),
|
|
198
|
-
Handlers: {
|
|
199
|
-
errorHandler: mock(() => (err: any, _req: any, _res: any, next: any) => next(err)),
|
|
200
|
-
requestHandler: mock(() => (_req: any, _res: any, next: any) => next()),
|
|
201
|
-
tracingHandler: mock(() => (_req: any, _res: any, next: any) => next()),
|
|
202
|
-
},
|
|
203
|
-
init: mockFn(),
|
|
204
|
-
isInitialized: mock(() => true),
|
|
205
|
-
popScope: mockFn(),
|
|
206
|
-
pushScope: mockFn(),
|
|
207
|
-
Severity: {
|
|
208
|
-
Debug: "debug",
|
|
209
|
-
Error: "error",
|
|
210
|
-
Fatal: "fatal",
|
|
211
|
-
Info: "info",
|
|
212
|
-
Warning: "warning",
|
|
213
|
-
} as const,
|
|
214
|
-
setContext: mockFn(),
|
|
215
|
-
setFingerprint: mockFn(),
|
|
216
|
-
setLevel: mockFn(),
|
|
217
|
-
setTag: mockFn(),
|
|
218
|
-
setTags: mockFn(),
|
|
219
|
-
setUser: mockFn(),
|
|
220
|
-
setupExpressErrorHandler: mockFn(),
|
|
221
|
-
startTransaction: mock(() => mockTransaction),
|
|
222
|
-
withScope: mock((callback: any) => callback(mockScope)),
|
|
223
|
-
},
|
|
224
|
-
flush: mock(() => Promise.resolve(true)),
|
|
225
|
-
getClient: mock(() => mockClient),
|
|
226
|
-
getCurrentHub: mock(() => mockHub),
|
|
227
|
-
getCurrentScope: mock(() => mockScope),
|
|
228
|
-
Handlers: {
|
|
229
|
-
errorHandler: mock(() => (err: any, _req: any, _res: any, next: any) => next(err)),
|
|
230
|
-
requestHandler: mock(() => (_req: any, _res: any, next: any) => next()),
|
|
231
|
-
tracingHandler: mock(() => (_req: any, _res: any, next: any) => next()),
|
|
232
|
-
},
|
|
233
|
-
init: mockFn(),
|
|
234
|
-
isInitialized: mock(() => true),
|
|
235
|
-
popScope: mockFn(),
|
|
236
|
-
pushScope: mockFn(),
|
|
237
|
-
Severity: {
|
|
238
|
-
Debug: "debug",
|
|
239
|
-
Error: "error",
|
|
240
|
-
Fatal: "fatal",
|
|
241
|
-
Info: "info",
|
|
242
|
-
Warning: "warning",
|
|
243
|
-
} as const,
|
|
244
|
-
setContext: mockFn(),
|
|
245
|
-
setFingerprint: mockFn(),
|
|
246
|
-
setLevel: mockFn(),
|
|
247
|
-
setTag: mockFn(),
|
|
248
|
-
setTags: mockFn(),
|
|
249
|
-
setUser: mockFn(),
|
|
250
|
-
setupExpressErrorHandler: mockFn(),
|
|
251
|
-
startTransaction: mock(() => mockTransaction),
|
|
252
|
-
withScope: mock((callback: any) => callback(mockScope)),
|
|
253
|
-
};
|
|
254
|
-
});
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type {HydratedDocument} from "mongoose";
|
|
2
|
+
import type {PassportLocalMongooseDocument} from "passport-local-mongoose";
|
|
3
|
+
|
|
4
|
+
import {logger} from "../logger";
|
|
5
|
+
import {FoodModel, RequiredModel, type User, UserModel} from "./models";
|
|
6
|
+
import type {CachedTestData, TestData, TestFoods, TestRequired, TestUsers} from "./types";
|
|
7
|
+
|
|
8
|
+
const setPassword = async (user: HydratedDocument<User>, password: string): Promise<void> => {
|
|
9
|
+
await (user as unknown as PassportLocalMongooseDocument).setPassword(password);
|
|
10
|
+
await user.save();
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const clearTestCollections = async (): Promise<void> => {
|
|
14
|
+
await Promise.all([
|
|
15
|
+
UserModel.deleteMany({}),
|
|
16
|
+
FoodModel.deleteMany({}),
|
|
17
|
+
RequiredModel.deleteMany({}),
|
|
18
|
+
]).catch(logger.catch);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const createTestUsers = async (): Promise<TestUsers> => {
|
|
22
|
+
const [notAdmin, admin, adminOther] = await Promise.all([
|
|
23
|
+
UserModel.create({email: "notAdmin@example.com", name: "Not Admin"}),
|
|
24
|
+
UserModel.create({admin: true, email: "admin@example.com", name: "Admin"}),
|
|
25
|
+
UserModel.create({admin: true, email: "admin+other@example.com", name: "Admin Other"}),
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
await Promise.all([
|
|
29
|
+
setPassword(notAdmin, "password"),
|
|
30
|
+
setPassword(admin, "securePassword"),
|
|
31
|
+
setPassword(adminOther, "otherPassword"),
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
return {admin, adminOther, notAdmin};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const createStandardFoods = async (users: TestUsers): Promise<TestFoods> => {
|
|
38
|
+
const {admin, adminOther, notAdmin} = users;
|
|
39
|
+
|
|
40
|
+
const [spinach, apple, carrots, pizza] = await Promise.all([
|
|
41
|
+
FoodModel.create({
|
|
42
|
+
calories: 1,
|
|
43
|
+
categories: [{name: "Vegetables", show: true}],
|
|
44
|
+
created: new Date("2021-12-03T00:00:20.000Z"),
|
|
45
|
+
eatenBy: [admin._id],
|
|
46
|
+
expiration: "2026-12-31",
|
|
47
|
+
hidden: false,
|
|
48
|
+
lastEatenWith: {
|
|
49
|
+
dressing: new Date("2021-12-03T19:00:30.000Z"),
|
|
50
|
+
},
|
|
51
|
+
likesIds: [
|
|
52
|
+
{likes: true, userId: admin._id},
|
|
53
|
+
{likes: false, userId: notAdmin._id},
|
|
54
|
+
],
|
|
55
|
+
name: "Spinach",
|
|
56
|
+
ownerId: notAdmin._id,
|
|
57
|
+
source: {
|
|
58
|
+
dateAdded: "2023-12-13T12:30:00.000Z",
|
|
59
|
+
href: "https://www.example.com/spinach",
|
|
60
|
+
name: "Brand",
|
|
61
|
+
},
|
|
62
|
+
tags: ["healthy"],
|
|
63
|
+
}),
|
|
64
|
+
FoodModel.create({
|
|
65
|
+
calories: 100,
|
|
66
|
+
created: new Date("2021-12-03T00:00:30.000Z"),
|
|
67
|
+
expiration: "2026-12-31",
|
|
68
|
+
hidden: true,
|
|
69
|
+
likesIds: [{likes: true, userId: admin._id}],
|
|
70
|
+
name: "Apple",
|
|
71
|
+
ownerId: admin._id,
|
|
72
|
+
source: {name: "Orchard"},
|
|
73
|
+
tags: ["healthy"],
|
|
74
|
+
}),
|
|
75
|
+
FoodModel.create({
|
|
76
|
+
calories: 100,
|
|
77
|
+
created: new Date("2021-12-03T00:00:00.000Z"),
|
|
78
|
+
eatenBy: [admin._id, notAdmin._id],
|
|
79
|
+
expiration: "2026-12-31",
|
|
80
|
+
hidden: false,
|
|
81
|
+
likesIds: [{likes: false, userId: notAdmin._id}],
|
|
82
|
+
name: "Carrots",
|
|
83
|
+
ownerId: admin._id,
|
|
84
|
+
source: {name: "Farm"},
|
|
85
|
+
tags: ["vegetable"],
|
|
86
|
+
}),
|
|
87
|
+
FoodModel.create({
|
|
88
|
+
calories: 800,
|
|
89
|
+
created: new Date("2022-01-01T00:00:00.000Z"),
|
|
90
|
+
expiration: "2026-12-31",
|
|
91
|
+
hidden: false,
|
|
92
|
+
likesIds: [{likes: true, userId: adminOther._id}],
|
|
93
|
+
name: "Pizza",
|
|
94
|
+
ownerId: adminOther._id,
|
|
95
|
+
source: {name: "Pizzeria"},
|
|
96
|
+
tags: ["comfort"],
|
|
97
|
+
}),
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
return {apple, carrots, pizza, spinach};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const createRequiredFixtures = async (): Promise<TestRequired> => {
|
|
104
|
+
const [sample, withAbout] = await Promise.all([
|
|
105
|
+
RequiredModel.create({name: "Sample Required"}),
|
|
106
|
+
RequiredModel.create({about: "Optional about text", name: "Required With About"}),
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
return {sample, withAbout};
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/** Builds the standard Terreno API test database (users, foods, required docs). */
|
|
113
|
+
export const createTestData = async (): Promise<TestData> => {
|
|
114
|
+
await clearTestCollections();
|
|
115
|
+
|
|
116
|
+
const users = await createTestUsers();
|
|
117
|
+
const [foods, required] = await Promise.all([
|
|
118
|
+
createStandardFoods(users),
|
|
119
|
+
createRequiredFixtures(),
|
|
120
|
+
]);
|
|
121
|
+
|
|
122
|
+
return {foods, required, users};
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const toCachedTestData = (testData: TestData): CachedTestData => ({
|
|
126
|
+
foods: {
|
|
127
|
+
apple: testData.foods.apple.id,
|
|
128
|
+
carrots: testData.foods.carrots.id,
|
|
129
|
+
pizza: testData.foods.pizza.id,
|
|
130
|
+
spinach: testData.foods.spinach.id,
|
|
131
|
+
},
|
|
132
|
+
required: {
|
|
133
|
+
sample: testData.required.sample.id,
|
|
134
|
+
withAbout: testData.required.withAbout.id,
|
|
135
|
+
},
|
|
136
|
+
users: {
|
|
137
|
+
admin: testData.users.admin.id,
|
|
138
|
+
adminOther: testData.users.adminOther.id,
|
|
139
|
+
notAdmin: testData.users.notAdmin.id,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
export const loadTestDataFromDocuments = async (cached: CachedTestData): Promise<TestData> => {
|
|
144
|
+
const [admin, notAdmin, adminOther, spinach, apple, carrots, pizza, sample, withAbout] =
|
|
145
|
+
await Promise.all([
|
|
146
|
+
UserModel.findById(cached.users.admin),
|
|
147
|
+
UserModel.findById(cached.users.notAdmin),
|
|
148
|
+
UserModel.findById(cached.users.adminOther),
|
|
149
|
+
FoodModel.findById(cached.foods.spinach),
|
|
150
|
+
FoodModel.findById(cached.foods.apple),
|
|
151
|
+
FoodModel.findById(cached.foods.carrots),
|
|
152
|
+
FoodModel.findById(cached.foods.pizza),
|
|
153
|
+
RequiredModel.findById(cached.required.sample),
|
|
154
|
+
RequiredModel.findById(cached.required.withAbout),
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
if (
|
|
158
|
+
!admin ||
|
|
159
|
+
!notAdmin ||
|
|
160
|
+
!adminOther ||
|
|
161
|
+
!spinach ||
|
|
162
|
+
!apple ||
|
|
163
|
+
!carrots ||
|
|
164
|
+
!pizza ||
|
|
165
|
+
!sample ||
|
|
166
|
+
!withAbout
|
|
167
|
+
) {
|
|
168
|
+
throw new Error("[createTestData] Cached test data references missing documents");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
foods: {apple, carrots, pizza, spinach},
|
|
173
|
+
required: {sample, withAbout},
|
|
174
|
+
users: {admin, adminOther, notAdmin},
|
|
175
|
+
};
|
|
176
|
+
};
|