@terreno/api 0.9.3 → 0.11.0
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 +5 -2
- package/bunfig.unit.toml +3 -0
- package/dist/auth.test.js +257 -0
- package/dist/consentApp.test.js +245 -0
- package/dist/expressServer.js +3 -9
- package/dist/expressServer.test.js +4 -7
- package/dist/githubAuth.test.js +380 -0
- package/dist/logger.test.d.ts +1 -0
- package/dist/logger.test.js +143 -0
- package/dist/notifiers/googleChatNotifier.test.js +37 -0
- package/dist/openApi.js +2 -2
- package/dist/openApi.test.js +125 -0
- package/dist/openApiBuilder.d.ts +1 -0
- package/dist/openApiBuilder.js +13 -2
- package/dist/openApiBuilder.test.js +66 -0
- package/dist/openApiEtag.test.js +8 -0
- package/dist/openApiValidator.test.js +309 -0
- package/dist/permissions.middleware.test.d.ts +1 -0
- package/dist/permissions.middleware.test.js +341 -0
- package/dist/plugins.d.ts +8 -8
- package/dist/plugins.js +38 -32
- package/dist/populate.test.js +99 -0
- package/dist/syncConsents.js +2 -2
- package/dist/syncConsents.test.js +273 -0
- package/dist/tests/bunSetup.js +27 -22
- package/dist/tests.d.ts +3 -3
- package/dist/tests.js +78 -82
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +7 -7
- package/package.json +2 -1
- package/src/__snapshots__/openApi.test.ts.snap +48 -0
- package/src/auth.test.ts +147 -0
- package/src/consentApp.test.ts +162 -0
- package/src/expressServer.test.ts +4 -11
- package/src/expressServer.ts +4 -8
- package/src/githubAuth.test.ts +307 -1
- package/src/logger.test.ts +149 -0
- package/src/notifiers/googleChatNotifier.test.ts +24 -0
- package/src/openApi.test.ts +157 -1
- package/src/openApi.ts +6 -2
- package/src/openApiBuilder.test.ts +81 -0
- package/src/openApiBuilder.ts +17 -2
- package/src/openApiEtag.test.ts +11 -0
- package/src/openApiValidator.test.ts +410 -0
- package/src/permissions.middleware.test.ts +197 -0
- package/src/plugins.ts +32 -23
- package/src/populate.test.ts +78 -2
- package/src/syncConsents.test.ts +145 -0
- package/src/syncConsents.ts +1 -1
- package/src/tests/bunSetup.ts +14 -8
- package/src/tests.ts +8 -8
- package/src/utils.ts +4 -4
package/src/plugins.ts
CHANGED
|
@@ -15,10 +15,12 @@ export interface BaseUser {
|
|
|
15
15
|
email: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export
|
|
19
|
-
schema.add({
|
|
20
|
-
|
|
21
|
-
}
|
|
18
|
+
export const baseUserPlugin = (schema: Schema<any, any, any, any>): void => {
|
|
19
|
+
schema.add({
|
|
20
|
+
admin: {default: false, description: "Whether the user has admin privileges", type: Boolean},
|
|
21
|
+
});
|
|
22
|
+
schema.add({email: {description: "The user's email address", index: true, type: String}});
|
|
23
|
+
};
|
|
22
24
|
|
|
23
25
|
/** For models with the isDeletedPlugin, extend this interface to add the appropriate fields. */
|
|
24
26
|
export interface IsDeleted {
|
|
@@ -26,7 +28,7 @@ export interface IsDeleted {
|
|
|
26
28
|
deleted: boolean;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
export
|
|
31
|
+
export const isDeletedPlugin = (schema: Schema<any, any, any, any>, defaultValue = false): void => {
|
|
30
32
|
schema.add({
|
|
31
33
|
deleted: {
|
|
32
34
|
default: defaultValue,
|
|
@@ -37,21 +39,24 @@ export function isDeletedPlugin(schema: Schema<any, any, any, any>, defaultValue
|
|
|
37
39
|
type: Boolean,
|
|
38
40
|
},
|
|
39
41
|
});
|
|
40
|
-
|
|
42
|
+
const applyDeleteFilter = (q: Query<any, any>): void => {
|
|
41
43
|
const query = q.getQuery();
|
|
42
44
|
if (query && query.deleted === undefined) {
|
|
43
45
|
void q.where({deleted: {$ne: true}});
|
|
44
46
|
}
|
|
45
|
-
}
|
|
47
|
+
};
|
|
46
48
|
schema.pre("find", function () {
|
|
47
49
|
applyDeleteFilter(this);
|
|
48
50
|
});
|
|
49
51
|
schema.pre("findOne", function () {
|
|
50
52
|
applyDeleteFilter(this);
|
|
51
53
|
});
|
|
52
|
-
}
|
|
54
|
+
};
|
|
53
55
|
|
|
54
|
-
export
|
|
56
|
+
export const isDisabledPlugin = (
|
|
57
|
+
schema: Schema<any, any, any, any>,
|
|
58
|
+
defaultValue = false
|
|
59
|
+
): void => {
|
|
55
60
|
schema.add({
|
|
56
61
|
disabled: {
|
|
57
62
|
default: defaultValue,
|
|
@@ -60,16 +65,18 @@ export function isDisabledPlugin(schema: Schema<any, any, any, any>, defaultValu
|
|
|
60
65
|
type: Boolean,
|
|
61
66
|
},
|
|
62
67
|
});
|
|
63
|
-
}
|
|
68
|
+
};
|
|
64
69
|
|
|
65
70
|
export interface CreatedDeleted {
|
|
66
71
|
updated: {type: Date; required: true};
|
|
67
72
|
created: {type: Date; required: true};
|
|
68
73
|
}
|
|
69
74
|
|
|
70
|
-
export
|
|
71
|
-
schema.add({
|
|
72
|
-
|
|
75
|
+
export const createdUpdatedPlugin = (schema: Schema<any, any, any, any>): void => {
|
|
76
|
+
schema.add({
|
|
77
|
+
updated: {description: "When this document was last updated", index: true, type: Date},
|
|
78
|
+
});
|
|
79
|
+
schema.add({created: {description: "When this document was created", index: true, type: Date}});
|
|
73
80
|
|
|
74
81
|
schema.pre("save", function () {
|
|
75
82
|
if (this.disableCreatedUpdatedPlugin === true) {
|
|
@@ -86,11 +93,13 @@ export function createdUpdatedPlugin(schema: Schema<any, any, any, any>) {
|
|
|
86
93
|
schema.pre(/save|updateOne|insertMany/, function () {
|
|
87
94
|
void this.updateOne({}, {$set: {updated: new Date()}});
|
|
88
95
|
});
|
|
89
|
-
}
|
|
96
|
+
};
|
|
90
97
|
|
|
91
|
-
export
|
|
92
|
-
schema.add({
|
|
93
|
-
}
|
|
98
|
+
export const firebaseJWTPlugin = (schema: Schema): void => {
|
|
99
|
+
schema.add({
|
|
100
|
+
firebaseId: {description: "The user's Firebase authentication ID", index: true, type: String},
|
|
101
|
+
});
|
|
102
|
+
};
|
|
94
103
|
|
|
95
104
|
/**
|
|
96
105
|
* This adds a static method `Model.findOneOrNone` to the schema. This should replace `Model.findOne` in most instances.
|
|
@@ -99,7 +108,7 @@ export function firebaseJWTPlugin(schema: Schema) {
|
|
|
99
108
|
* document, or throws an exception if multiple are found.
|
|
100
109
|
* @param schema Mongoose Schema
|
|
101
110
|
*/
|
|
102
|
-
export
|
|
111
|
+
export const findOneOrNone = <T>(schema: Schema<T>): void => {
|
|
103
112
|
schema.statics.findOneOrNone = async function (
|
|
104
113
|
query: Record<string, any>,
|
|
105
114
|
errorArgs?: Partial<APIErrorConstructor>
|
|
@@ -118,7 +127,7 @@ export function findOneOrNone<T>(schema: Schema<T>) {
|
|
|
118
127
|
}
|
|
119
128
|
return results[0];
|
|
120
129
|
};
|
|
121
|
-
}
|
|
130
|
+
};
|
|
122
131
|
|
|
123
132
|
/**
|
|
124
133
|
* This adds a static method `Model.findExactlyOne` to the schema. This or findOneOrNone should replace `Model.findOne`
|
|
@@ -128,7 +137,7 @@ export function findOneOrNone<T>(schema: Schema<T>) {
|
|
|
128
137
|
* multiple or none are found.
|
|
129
138
|
* @param schema Mongoose Schema
|
|
130
139
|
*/
|
|
131
|
-
export
|
|
140
|
+
export const findExactlyOne = <T>(schema: Schema<T>): void => {
|
|
132
141
|
schema.statics.findExactlyOne = async function (
|
|
133
142
|
query: Record<string, any>,
|
|
134
143
|
errorArgs?: Partial<APIErrorConstructor>
|
|
@@ -152,7 +161,7 @@ export function findExactlyOne<T>(schema: Schema<T>) {
|
|
|
152
161
|
}
|
|
153
162
|
return results[0];
|
|
154
163
|
};
|
|
155
|
-
}
|
|
164
|
+
};
|
|
156
165
|
|
|
157
166
|
/**
|
|
158
167
|
* This adds a static method `Model.upsert` to the schema. This method will either update an existing document
|
|
@@ -160,7 +169,7 @@ export function findExactlyOne<T>(schema: Schema<T>) {
|
|
|
160
169
|
* match the conditions to prevent ambiguous updates.
|
|
161
170
|
* @param schema Mongoose Schema
|
|
162
171
|
*/
|
|
163
|
-
export
|
|
172
|
+
export const upsertPlugin = <T>(schema: Schema<any, any, any, any>): void => {
|
|
164
173
|
schema.statics.upsert = async function (
|
|
165
174
|
conditions: Record<string, any>,
|
|
166
175
|
update: Record<string, any>
|
|
@@ -187,7 +196,7 @@ export function upsertPlugin<T>(schema: Schema<any, any, any, any>) {
|
|
|
187
196
|
const newDoc = new this(combinedData);
|
|
188
197
|
return newDoc.save();
|
|
189
198
|
};
|
|
190
|
-
}
|
|
199
|
+
};
|
|
191
200
|
|
|
192
201
|
/** For models with the upsertPlugin, extend this interface to add the upsert static method. */
|
|
193
202
|
export interface HasUpsert<T> {
|
package/src/populate.test.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {beforeEach, describe, expect, it} from "bun:test";
|
|
2
|
+
import mongoose, {Schema} from "mongoose";
|
|
2
3
|
|
|
3
|
-
import {unpopulate} from "./populate";
|
|
4
|
-
import {FoodModel, setupDb} from "./tests";
|
|
4
|
+
import {fixMixedFields, getOpenApiSpecForModel, unpopulate} from "./populate";
|
|
5
|
+
import {FoodModel, setupDb, UserModel} from "./tests";
|
|
5
6
|
|
|
6
7
|
describe("populate functions", () => {
|
|
7
8
|
let admin: any;
|
|
@@ -121,3 +122,78 @@ describe("unpopulate edge cases", () => {
|
|
|
121
122
|
expect(result.containers[1].items).toEqual(["item-3", "item-4"]);
|
|
122
123
|
});
|
|
123
124
|
});
|
|
125
|
+
|
|
126
|
+
describe("fixMixedFields", () => {
|
|
127
|
+
it("returns early when schema is missing", () => {
|
|
128
|
+
const properties = {foo: {type: "object"}};
|
|
129
|
+
expect(() => fixMixedFields(null, properties)).not.toThrow();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("returns early when properties is missing", () => {
|
|
133
|
+
const schema = new Schema({});
|
|
134
|
+
expect(() => fixMixedFields(schema, null as any)).not.toThrow();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("replaces Mixed fields with only description", () => {
|
|
138
|
+
const schema = new Schema({data: {description: "any data", type: Schema.Types.Mixed}});
|
|
139
|
+
const properties: any = {data: {description: "any data", type: "object"}};
|
|
140
|
+
fixMixedFields(schema, properties);
|
|
141
|
+
expect(properties.data).toEqual({description: "any data"});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("recurses into arrays of sub-documents", () => {
|
|
145
|
+
const subSchema = new Schema({meta: {type: Schema.Types.Mixed}});
|
|
146
|
+
const schema = new Schema({items: [subSchema]});
|
|
147
|
+
const properties: any = {
|
|
148
|
+
items: {
|
|
149
|
+
items: {
|
|
150
|
+
properties: {
|
|
151
|
+
meta: {type: "object"},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
type: "array",
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
fixMixedFields(schema, properties);
|
|
158
|
+
expect(properties.items.items.properties.meta).toEqual({description: undefined});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("skips unknown paths", () => {
|
|
162
|
+
const schema = new Schema({foo: String});
|
|
163
|
+
const properties = {unknownKey: {type: "string"}};
|
|
164
|
+
expect(() => fixMixedFields(schema, properties)).not.toThrow();
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe("getOpenApiSpecForModel edge cases", () => {
|
|
169
|
+
it("returns model properties without populatePaths", () => {
|
|
170
|
+
const result = getOpenApiSpecForModel(UserModel);
|
|
171
|
+
expect(result.properties).toBeDefined();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("returns with extraModelProperties merged", () => {
|
|
175
|
+
const result = getOpenApiSpecForModel(UserModel, {
|
|
176
|
+
extraModelProperties: {customField: {type: "string"}},
|
|
177
|
+
});
|
|
178
|
+
expect(result.properties.customField).toEqual({type: "string"});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("skips populate paths without ref", () => {
|
|
182
|
+
// Create a schema with a non-referenced ObjectId field
|
|
183
|
+
const testSchema = new Schema({name: String, simpleId: Schema.Types.ObjectId});
|
|
184
|
+
const TestModelNoRef =
|
|
185
|
+
mongoose.models.TestModelNoRef || mongoose.model("TestModelNoRef", testSchema);
|
|
186
|
+
const result = getOpenApiSpecForModel(TestModelNoRef, {
|
|
187
|
+
populatePaths: [{path: "simpleId"}],
|
|
188
|
+
});
|
|
189
|
+
// Should not throw, simpleId stays as-is
|
|
190
|
+
expect(result.properties).toBeDefined();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("populates with fields allowlist", () => {
|
|
194
|
+
const result = getOpenApiSpecForModel(FoodModel, {
|
|
195
|
+
populatePaths: [{fields: ["name"], path: "ownerId"}],
|
|
196
|
+
});
|
|
197
|
+
expect(result.properties).toBeDefined();
|
|
198
|
+
});
|
|
199
|
+
});
|
package/src/syncConsents.test.ts
CHANGED
|
@@ -121,4 +121,149 @@ describe("syncConsents", () => {
|
|
|
121
121
|
expect(forms[0].slug).toBe("terms");
|
|
122
122
|
expect(forms[1].slug).toBe("privacy");
|
|
123
123
|
});
|
|
124
|
+
|
|
125
|
+
it("publishes new version when type changes", async () => {
|
|
126
|
+
await syncConsents({terms: baseDef});
|
|
127
|
+
const updated: ConsentFormDefinition = {...baseDef, type: "privacy"};
|
|
128
|
+
const result = await syncConsents({terms: updated});
|
|
129
|
+
expect(result.updated).toEqual(["terms"]);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("publishes new version when order changes", async () => {
|
|
133
|
+
await syncConsents({terms: baseDef});
|
|
134
|
+
const updated = {...baseDef, order: 99};
|
|
135
|
+
const result = await syncConsents({terms: updated});
|
|
136
|
+
expect(result.updated).toEqual(["terms"]);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("publishes new version when required changes", async () => {
|
|
140
|
+
await syncConsents({terms: baseDef});
|
|
141
|
+
const updated = {...baseDef, required: false};
|
|
142
|
+
const result = await syncConsents({terms: updated});
|
|
143
|
+
expect(result.updated).toEqual(["terms"]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("publishes new version when requireScrollToBottom changes", async () => {
|
|
147
|
+
await syncConsents({terms: baseDef});
|
|
148
|
+
const updated = {...baseDef, requireScrollToBottom: true};
|
|
149
|
+
const result = await syncConsents({terms: updated});
|
|
150
|
+
expect(result.updated).toEqual(["terms"]);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("publishes new version when captureSignature changes", async () => {
|
|
154
|
+
await syncConsents({terms: baseDef});
|
|
155
|
+
const updated = {...baseDef, captureSignature: true};
|
|
156
|
+
const result = await syncConsents({terms: updated});
|
|
157
|
+
expect(result.updated).toEqual(["terms"]);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("publishes new version when agreeButtonText changes", async () => {
|
|
161
|
+
await syncConsents({terms: baseDef});
|
|
162
|
+
const updated = {...baseDef, agreeButtonText: "Consent"};
|
|
163
|
+
const result = await syncConsents({terms: updated});
|
|
164
|
+
expect(result.updated).toEqual(["terms"]);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("publishes new version when allowDecline changes", async () => {
|
|
168
|
+
await syncConsents({terms: baseDef});
|
|
169
|
+
const updated = {...baseDef, allowDecline: true};
|
|
170
|
+
const result = await syncConsents({terms: updated});
|
|
171
|
+
expect(result.updated).toEqual(["terms"]);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("publishes new version when declineButtonText changes", async () => {
|
|
175
|
+
await syncConsents({terms: baseDef});
|
|
176
|
+
const updated = {...baseDef, allowDecline: true, declineButtonText: "No Thanks"};
|
|
177
|
+
const result = await syncConsents({terms: updated});
|
|
178
|
+
expect(result.updated).toEqual(["terms"]);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("publishes new version when defaultLocale changes", async () => {
|
|
182
|
+
await syncConsents({terms: baseDef});
|
|
183
|
+
const updated = {...baseDef, defaultLocale: "es"};
|
|
184
|
+
const result = await syncConsents({terms: updated});
|
|
185
|
+
expect(result.updated).toEqual(["terms"]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("publishes new version when content locale count changes", async () => {
|
|
189
|
+
await syncConsents({terms: baseDef});
|
|
190
|
+
const updated = {...baseDef, content: {en: baseDef.content.en, es: "# Términos"}};
|
|
191
|
+
const result = await syncConsents({terms: updated});
|
|
192
|
+
expect(result.updated).toEqual(["terms"]);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("publishes new version when checkbox count changes", async () => {
|
|
196
|
+
await syncConsents({
|
|
197
|
+
terms: {
|
|
198
|
+
...baseDef,
|
|
199
|
+
checkboxes: [{label: "Agree", required: true}],
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
const updated = {
|
|
203
|
+
...baseDef,
|
|
204
|
+
checkboxes: [
|
|
205
|
+
{label: "Agree", required: true},
|
|
206
|
+
{label: "Also agree", required: false},
|
|
207
|
+
],
|
|
208
|
+
};
|
|
209
|
+
const result = await syncConsents({terms: updated});
|
|
210
|
+
expect(result.updated).toEqual(["terms"]);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("publishes new version when checkbox label changes", async () => {
|
|
214
|
+
await syncConsents({
|
|
215
|
+
terms: {
|
|
216
|
+
...baseDef,
|
|
217
|
+
checkboxes: [{label: "Agree", required: true}],
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
const updated = {
|
|
221
|
+
...baseDef,
|
|
222
|
+
checkboxes: [{label: "I Agree", required: true}],
|
|
223
|
+
};
|
|
224
|
+
const result = await syncConsents({terms: updated});
|
|
225
|
+
expect(result.updated).toEqual(["terms"]);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("publishes new version when checkbox confirmationPrompt changes", async () => {
|
|
229
|
+
await syncConsents({
|
|
230
|
+
terms: {
|
|
231
|
+
...baseDef,
|
|
232
|
+
checkboxes: [{confirmationPrompt: "Sure?", label: "Agree", required: true}],
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
const updated = {
|
|
236
|
+
...baseDef,
|
|
237
|
+
checkboxes: [{confirmationPrompt: "Are you sure?", label: "Agree", required: true}],
|
|
238
|
+
};
|
|
239
|
+
const result = await syncConsents({terms: updated});
|
|
240
|
+
expect(result.updated).toEqual(["terms"]);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("leaves unchanged forms alone with checkboxes present", async () => {
|
|
244
|
+
const withCheckboxes = {
|
|
245
|
+
...baseDef,
|
|
246
|
+
checkboxes: [{confirmationPrompt: "Sure?", label: "Agree", required: true}],
|
|
247
|
+
};
|
|
248
|
+
await syncConsents({terms: withCheckboxes});
|
|
249
|
+
const result = await syncConsents({terms: withCheckboxes});
|
|
250
|
+
expect(result.unchanged).toEqual(["terms"]);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("dry run does not create new versions", async () => {
|
|
254
|
+
await syncConsents({terms: baseDef});
|
|
255
|
+
const updated = {...baseDef, title: "Updated"};
|
|
256
|
+
const result = await syncConsents({terms: updated}, {dryRun: true});
|
|
257
|
+
expect(result.updated).toEqual(["terms"]);
|
|
258
|
+
const forms = await ConsentForm.find({slug: "terms"});
|
|
259
|
+
expect(forms).toHaveLength(1); // No new version created
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("dry run does not deactivate forms", async () => {
|
|
263
|
+
await syncConsents({privacy: {...baseDef, title: "Privacy", type: "privacy"}, terms: baseDef});
|
|
264
|
+
const result = await syncConsents({terms: baseDef}, {deactivateRemoved: true, dryRun: true});
|
|
265
|
+
expect(result.deactivated).toEqual(["privacy"]);
|
|
266
|
+
const privacy = await ConsentForm.findOne({slug: "privacy"});
|
|
267
|
+
expect(privacy?.active).toBe(true); // Still active
|
|
268
|
+
});
|
|
124
269
|
});
|
package/src/syncConsents.ts
CHANGED
|
@@ -237,7 +237,7 @@ export const syncConsents = async (
|
|
|
237
237
|
|
|
238
238
|
// Deactivate forms that are no longer in definitions
|
|
239
239
|
if (deactivateRemoved) {
|
|
240
|
-
for (const [slug
|
|
240
|
+
for (const [slug] of activeBySlug) {
|
|
241
241
|
if (!definitions[slug]) {
|
|
242
242
|
logger.info(`syncConsents: deactivating "${slug}"`, {dryRun});
|
|
243
243
|
if (!dryRun) {
|
package/src/tests/bunSetup.ts
CHANGED
|
@@ -6,17 +6,23 @@ import winston from "winston";
|
|
|
6
6
|
import {setupEnvironment} from "../expressServer";
|
|
7
7
|
import {logger, winstonLogger} from "../logger";
|
|
8
8
|
|
|
9
|
+
const shouldConnectToTestDb = process.env.BUN_TEST_DISABLE_DB !== "true";
|
|
10
|
+
|
|
9
11
|
// Connect to MongoDB once for all tests
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
if (shouldConnectToTestDb) {
|
|
13
|
+
beforeAll(async () => {
|
|
14
|
+
await mongoose
|
|
15
|
+
.connect("mongodb://127.0.0.1/terreno?&connectTimeoutMS=360000")
|
|
16
|
+
.catch(logger.catch);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
15
19
|
|
|
16
20
|
// Close MongoDB connection after all tests
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
if (shouldConnectToTestDb) {
|
|
22
|
+
afterAll(async () => {
|
|
23
|
+
await mongoose.connection.close();
|
|
24
|
+
});
|
|
25
|
+
}
|
|
20
26
|
|
|
21
27
|
let logs: string[] = [];
|
|
22
28
|
|
package/src/tests.ts
CHANGED
|
@@ -162,7 +162,7 @@ const requiredSchema = new Schema<RequiredField>({
|
|
|
162
162
|
});
|
|
163
163
|
export const RequiredModel = model<RequiredField>("Required", requiredSchema);
|
|
164
164
|
|
|
165
|
-
export
|
|
165
|
+
export const getBaseServer = (): Express => {
|
|
166
166
|
const app = express();
|
|
167
167
|
app.set("query parser", (str: string) => qs.parse(str, {arrayLimit: 200}));
|
|
168
168
|
|
|
@@ -186,12 +186,12 @@ export function getBaseServer(): Express {
|
|
|
186
186
|
});
|
|
187
187
|
app.use(express.json());
|
|
188
188
|
return app;
|
|
189
|
-
}
|
|
189
|
+
};
|
|
190
190
|
|
|
191
|
-
export async
|
|
191
|
+
export const authAsUser = async (
|
|
192
192
|
app: express.Application,
|
|
193
193
|
type: "admin" | "notAdmin"
|
|
194
|
-
): Promise<TestAgent> {
|
|
194
|
+
): Promise<TestAgent> => {
|
|
195
195
|
const email = type === "admin" ? "admin@example.com" : "notAdmin@example.com";
|
|
196
196
|
const password = type === "admin" ? "securePassword" : "password";
|
|
197
197
|
|
|
@@ -199,9 +199,9 @@ export async function authAsUser(
|
|
|
199
199
|
const res = await agent.post("/auth/login").send({email, password}).expect(200);
|
|
200
200
|
await agent.set("authorization", `Bearer ${res.body.data.token}`);
|
|
201
201
|
return agent;
|
|
202
|
-
}
|
|
202
|
+
};
|
|
203
203
|
|
|
204
|
-
export async
|
|
204
|
+
export const setupDb = async () => {
|
|
205
205
|
await mongoose
|
|
206
206
|
.connect("mongodb://127.0.0.1/terreno?&connectTimeoutMS=360000")
|
|
207
207
|
.catch(logger.catch);
|
|
@@ -233,7 +233,7 @@ export async function setupDb() {
|
|
|
233
233
|
|
|
234
234
|
return [admin, notAdmin, adminOther];
|
|
235
235
|
} catch (error) {
|
|
236
|
-
|
|
236
|
+
logger.error("Error setting up DB", error);
|
|
237
237
|
throw error;
|
|
238
238
|
}
|
|
239
|
-
}
|
|
239
|
+
};
|
package/src/utils.ts
CHANGED
|
@@ -4,14 +4,14 @@ import {logger} from "./logger";
|
|
|
4
4
|
|
|
5
5
|
// A better version of mongoose's ObjectId.isValid,
|
|
6
6
|
// which falsely will say any 12 character string is valid.
|
|
7
|
-
export
|
|
7
|
+
export const isValidObjectId = (id: string): boolean => {
|
|
8
8
|
try {
|
|
9
9
|
return new Types.ObjectId(id).toString() === id;
|
|
10
10
|
} catch (error) {
|
|
11
11
|
logger.error(`Error validating object id ${id}: ${error}`);
|
|
12
12
|
return false;
|
|
13
13
|
}
|
|
14
|
-
}
|
|
14
|
+
};
|
|
15
15
|
|
|
16
16
|
export const timeout = async (ms: number): Promise<NodeJS.Timeout> => {
|
|
17
17
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -25,7 +25,7 @@ export const timeout = async (ms: number): Promise<NodeJS.Timeout> => {
|
|
|
25
25
|
* @param ignoredModels - Array of model names to skip validation for
|
|
26
26
|
* @throws Error if any model is not set to strict mode or missing virtual settings
|
|
27
27
|
*/
|
|
28
|
-
export
|
|
28
|
+
export const checkModelsStrict = (ignoredModels: string[] = []): void => {
|
|
29
29
|
const models = mongoose.modelNames();
|
|
30
30
|
for (const model of models) {
|
|
31
31
|
const schema = mongoose.model(model).schema;
|
|
@@ -44,4 +44,4 @@ export function checkModelsStrict(ignoredModels: string[] = []): void {
|
|
|
44
44
|
throw new Error(`Model ${model} is not set to strict mode.`);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
}
|
|
47
|
+
};
|