liwi-mongo 13.3.3 → 14.0.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.
Files changed (40) hide show
  1. package/README.md +27 -2
  2. package/dist/definitions/MongoConnection.d.ts +7 -1
  3. package/dist/definitions/MongoConnection.d.ts.map +1 -1
  4. package/dist/definitions/MongoConnection.test.d.ts +2 -0
  5. package/dist/definitions/MongoConnection.test.d.ts.map +1 -0
  6. package/dist/definitions/MongoCursor.d.ts.map +1 -1
  7. package/dist/definitions/MongoQuerySingleItem.d.ts +1 -0
  8. package/dist/definitions/MongoQuerySingleItem.d.ts.map +1 -1
  9. package/dist/definitions/MongoQuerySingleItem.test.d.ts +2 -0
  10. package/dist/definitions/MongoQuerySingleItem.test.d.ts.map +1 -0
  11. package/dist/definitions/MongoStore.d.ts +18 -4
  12. package/dist/definitions/MongoStore.d.ts.map +1 -1
  13. package/dist/definitions/SubscribeStore.integration.test.d.ts +2 -0
  14. package/dist/definitions/SubscribeStore.integration.test.d.ts.map +1 -0
  15. package/dist/definitions/writes/createReplacePipeline.d.ts +10 -0
  16. package/dist/definitions/writes/createReplacePipeline.d.ts.map +1 -0
  17. package/dist/definitions/writes/stampPartialUpdate.d.ts +9 -0
  18. package/dist/definitions/writes/stampPartialUpdate.d.ts.map +1 -0
  19. package/dist/definitions/writes/stampPartialUpdate.test.d.ts +2 -0
  20. package/dist/definitions/writes/stampPartialUpdate.test.d.ts.map +1 -0
  21. package/dist/index-node.mjs +225 -93
  22. package/dist/index-node.mjs.map +1 -1
  23. package/package.json +9 -8
  24. package/src/MongoConnection.test.ts +75 -0
  25. package/src/MongoConnection.ts +38 -25
  26. package/src/MongoCursor.ts +2 -4
  27. package/src/MongoQueryCollection.test.ts +9 -23
  28. package/src/MongoQueryCollection.ts +2 -2
  29. package/src/MongoQuerySingleItem.test.ts +162 -0
  30. package/src/MongoQuerySingleItem.ts +58 -52
  31. package/src/MongoStore.test.ts +259 -18
  32. package/src/MongoStore.ts +122 -26
  33. package/src/MongoStoreIndexes.test.ts +10 -24
  34. package/src/SubscribeStore.integration.test.ts +414 -0
  35. package/src/indexes/diffIndexes.test.ts +2 -2
  36. package/src/indexes/normalizeIndex.test.ts +2 -2
  37. package/src/indexes/normalizeIndex.ts +2 -2
  38. package/src/writes/createReplacePipeline.ts +23 -0
  39. package/src/writes/stampPartialUpdate.test.ts +93 -0
  40. package/src/writes/stampPartialUpdate.ts +84 -0
@@ -1,6 +1,11 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { beforeEach, describe, it, mock } from "node:test";
3
+ import { NotFoundError, ProtectedFieldUpdateError } from "liwi-store";
4
+ import mongodb from "mongodb";
3
5
  import type { MongoBaseModel } from "./MongoBaseModel.ts";
6
+ import MongoConnection from "./MongoConnection.ts";
7
+ import MongoStore from "./MongoStore.ts";
8
+ import { createReplacePipeline } from "./writes/createReplacePipeline.ts";
4
9
 
5
10
  interface TestModel extends MongoBaseModel {
6
11
  label: string;
@@ -8,33 +13,34 @@ interface TestModel extends MongoBaseModel {
8
13
  }
9
14
 
10
15
  let findOneAndUpdateCalls: unknown[][];
16
+ let findOneAndDeleteCalls: unknown[][];
17
+ let updateManyCalls: unknown[][];
11
18
  let existingDocument: TestModel | null;
19
+ let documentAfterWrite: TestModel | null;
12
20
 
13
21
  const collection = {
14
22
  findOneAndUpdate: (...args: unknown[]) => {
15
23
  findOneAndUpdateCalls.push(args);
16
24
  return Promise.resolve(existingDocument);
17
25
  },
18
- };
19
-
20
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
21
- mock.module("mongodb", {
22
- // @ts-expect-error: not yet typed
23
- exports: {
24
- default: {
25
- MongoClient: {
26
- connect: () =>
27
- Promise.resolve({
28
- on: () => undefined,
29
- db: () => ({ collection: () => collection }),
30
- }),
31
- },
32
- },
26
+ findOneAndDelete: (...args: unknown[]) => {
27
+ findOneAndDeleteCalls.push(args);
28
+ return Promise.resolve(existingDocument);
33
29
  },
34
- });
30
+ findOne: () => Promise.resolve(documentAfterWrite),
31
+ updateMany: (...args: unknown[]) => {
32
+ updateManyCalls.push(args);
33
+ return Promise.resolve({ matchedCount: 1 });
34
+ },
35
+ deleteOne: () => Promise.resolve({ deletedCount: existingDocument ? 1 : 0 }),
36
+ };
35
37
 
36
- const { default: MongoConnection } = await import("./MongoConnection.ts");
37
- const { default: MongoStore } = await import("./MongoStore.ts");
38
+ mock.method(mongodb.MongoClient, "connect", () =>
39
+ Promise.resolve({
40
+ on: () => undefined,
41
+ db: () => ({ collection: () => collection }),
42
+ } as unknown as mongodb.MongoClient),
43
+ );
38
44
 
39
45
  const createStore = (): InstanceType<typeof MongoStore<TestModel>> =>
40
46
  new MongoStore<TestModel>(new MongoConnection({ database: "test" }), "tasks");
@@ -106,4 +112,239 @@ describe("MongoStore upsertOneWithInfo", () => {
106
112
  prev: existingDocument,
107
113
  });
108
114
  });
115
+
116
+ it("computes next with undefined values as the driver stores them", async () => {
117
+ existingDocument = {
118
+ _id: "1",
119
+ label: "old",
120
+ status: "x",
121
+ created,
122
+ updated,
123
+ };
124
+
125
+ const result = await createStore().upsertOneWithInfo({
126
+ _id: "1",
127
+ label: "a",
128
+ status: undefined,
129
+ updated,
130
+ });
131
+
132
+ assert.equal(result.object.status, null);
133
+ });
134
+
135
+ it("rejects updated in setOnInsert, which would conflict with $set", async () => {
136
+ await assert.rejects(
137
+ createStore().upsertOneWithInfo(
138
+ { _id: "1", label: "a" },
139
+ { updated: new Date() },
140
+ ),
141
+ ProtectedFieldUpdateError,
142
+ );
143
+ assert.deepEqual(findOneAndUpdateCalls, []);
144
+ });
145
+ });
146
+
147
+ describe("MongoStore writes with info", () => {
148
+ const stored: TestModel = { _id: "1", label: "stored", created, updated };
149
+ const next: TestModel = { _id: "1", label: "next", created, updated };
150
+ let writeStart: number;
151
+
152
+ const assertWriteTime = (value: unknown): Date => {
153
+ assert.ok(value instanceof Date);
154
+ assert.ok(value.getTime() >= writeStart && value.getTime() <= Date.now());
155
+ return value;
156
+ };
157
+
158
+ const stampOf = (update: unknown): unknown =>
159
+ (update as { $set: { updated: unknown } }).$set.updated;
160
+
161
+ const replacementStampOf = (pipeline: unknown): unknown =>
162
+ (
163
+ pipeline as [
164
+ {
165
+ $replaceWith: {
166
+ $mergeObjects: [{ $literal: { updated: unknown } }];
167
+ };
168
+ },
169
+ ]
170
+ )[0].$replaceWith.$mergeObjects[0].$literal.updated;
171
+
172
+ beforeEach(() => {
173
+ writeStart = Date.now();
174
+ findOneAndUpdateCalls = [];
175
+ findOneAndDeleteCalls = [];
176
+ updateManyCalls = [];
177
+ existingDocument = stored;
178
+ documentAfterWrite = next;
179
+ });
180
+
181
+ it("partially updates atomically with the criteria and the write time, then reads next", async () => {
182
+ const result = await createStore().partialUpdateByKeyWithInfo(
183
+ "1",
184
+ { $set: { label: "next" } },
185
+ { status: "open" },
186
+ );
187
+
188
+ const writeTime = assertWriteTime(stampOf(findOneAndUpdateCalls[0]?.[1]));
189
+ assert.deepEqual(findOneAndUpdateCalls, [
190
+ [
191
+ { _id: "1", status: "open" },
192
+ { $set: { label: "next", updated: writeTime } },
193
+ { returnDocument: "before" },
194
+ ],
195
+ ]);
196
+ assert.deepEqual(result, { prev: stored, next });
197
+ });
198
+
199
+ it("does not stamp updated with touchUpdated: false", async () => {
200
+ await createStore().partialUpdateByKey(
201
+ "1",
202
+ { $set: { label: "next" } },
203
+ undefined,
204
+ { touchUpdated: false },
205
+ );
206
+
207
+ assert.deepEqual(findOneAndUpdateCalls, [
208
+ [{ _id: "1" }, { $set: { label: "next" } }, { returnDocument: "after" }],
209
+ ]);
210
+ });
211
+
212
+ it("rejects a protected field update before writing", async () => {
213
+ await assert.rejects(
214
+ createStore().partialUpdateByKey("1", { $set: { created: new Date() } }),
215
+ ProtectedFieldUpdateError,
216
+ );
217
+ assert.deepEqual(findOneAndUpdateCalls, []);
218
+ });
219
+
220
+ it("partially updates many documents in one command with one write time", async () => {
221
+ await createStore().partialUpdateMany(
222
+ { status: "open" },
223
+ { $set: { label: "next" } },
224
+ );
225
+
226
+ const writeTime = assertWriteTime(stampOf(updateManyCalls[0]?.[1]));
227
+ assert.deepEqual(updateManyCalls, [
228
+ [{ status: "open" }, { $set: { label: "next", updated: writeTime } }],
229
+ ]);
230
+ });
231
+
232
+ it("resolves next undefined when the document is deleted before the read-back", async () => {
233
+ documentAfterWrite = null;
234
+
235
+ const result = await createStore().partialUpdateByKeyWithInfo("1", {
236
+ $set: { label: "next" },
237
+ });
238
+
239
+ assert.deepEqual(result, { prev: stored, next: undefined });
240
+ });
241
+
242
+ it("resolves undefined when the partial update matches nothing", async () => {
243
+ existingDocument = null;
244
+
245
+ const store = createStore();
246
+
247
+ assert.equal(
248
+ await store.partialUpdateByKeyWithInfo("1", { $set: { label: "x" } }),
249
+ undefined,
250
+ );
251
+ await assert.rejects(
252
+ store.partialUpdateByKey("1", { $set: { label: "x" } }),
253
+ NotFoundError,
254
+ );
255
+ });
256
+
257
+ it("replaces atomically, keeping the stored created and stamping updated", async () => {
258
+ const callerCreated = new Date("2025-01-01");
259
+ const object: TestModel = {
260
+ _id: "1",
261
+ label: "next",
262
+ created: callerCreated,
263
+ updated,
264
+ };
265
+
266
+ const result = await createStore().replaceOneWithInfo(object);
267
+
268
+ const writeTime = assertWriteTime(
269
+ replacementStampOf(findOneAndUpdateCalls[0]?.[1]),
270
+ );
271
+ const replacement = { ...object, updated: writeTime };
272
+ assert.deepEqual(findOneAndUpdateCalls, [
273
+ [
274
+ { _id: "1" },
275
+ createReplacePipeline(replacement),
276
+ { returnDocument: "before" },
277
+ ],
278
+ ]);
279
+ assert.deepEqual(result, {
280
+ prev: stored,
281
+ next: { ...replacement, created },
282
+ });
283
+ assert.equal(object.updated, updated);
284
+ });
285
+
286
+ it("partially updates in one call returning the document after the write", async () => {
287
+ existingDocument = next;
288
+
289
+ const result = await createStore().partialUpdateByKey("1", {
290
+ $set: { label: "next" },
291
+ });
292
+
293
+ const writeTime = assertWriteTime(stampOf(findOneAndUpdateCalls[0]?.[1]));
294
+ assert.deepEqual(findOneAndUpdateCalls, [
295
+ [
296
+ { _id: "1" },
297
+ { $set: { label: "next", updated: writeTime } },
298
+ { returnDocument: "after" },
299
+ ],
300
+ ]);
301
+ assert.deepEqual(result, next);
302
+ });
303
+
304
+ it("replaceOne returns the stored document and leaves the caller's object untouched", async () => {
305
+ existingDocument = next;
306
+ const object: TestModel = { _id: "1", label: "next", created, updated };
307
+
308
+ const result = await createStore().replaceOne(object);
309
+
310
+ const writeTime = assertWriteTime(
311
+ replacementStampOf(findOneAndUpdateCalls[0]?.[1]),
312
+ );
313
+ assert.deepEqual(findOneAndUpdateCalls, [
314
+ [
315
+ { _id: "1" },
316
+ createReplacePipeline({ ...object, updated: writeTime }),
317
+ { returnDocument: "after" },
318
+ ],
319
+ ]);
320
+ assert.equal(result, next);
321
+ assert.equal(object.updated, updated);
322
+ });
323
+
324
+ it("throws NotFoundError when replacing a missing document", async () => {
325
+ existingDocument = null;
326
+
327
+ await assert.rejects(
328
+ createStore().replaceOne({ _id: "1", label: "x", created, updated }),
329
+ NotFoundError,
330
+ );
331
+ });
332
+
333
+ it("deletes atomically with the criteria and returns prev", async () => {
334
+ const result = await createStore().deleteByKeyWithInfo("1", {
335
+ status: "open",
336
+ });
337
+
338
+ assert.deepEqual(findOneAndDeleteCalls, [[{ _id: "1", status: "open" }]]);
339
+ assert.deepEqual(result, { prev: stored });
340
+ });
341
+
342
+ it("resolves when deleting a missing document", async () => {
343
+ existingDocument = null;
344
+
345
+ const store = createStore();
346
+
347
+ assert.equal(await store.deleteByKeyWithInfo("1"), undefined);
348
+ await store.deleteByKey("1");
349
+ });
109
350
  });
package/src/MongoStore.ts CHANGED
@@ -1,15 +1,20 @@
1
+ import { NotFoundError, ProtectedFieldUpdateError } from "liwi-store";
1
2
  import type {
2
3
  AllowedKeyValue,
3
4
  CreateQueryOptions,
4
5
  Criteria,
6
+ DeleteResult,
5
7
  Fields,
6
8
  OptionalBaseModelKeysForInsert,
9
+ PartialUpdateOptions,
10
+ PartialUpdateResult,
7
11
  QueryParams,
8
12
  Sort,
9
13
  SubscribableStore,
10
14
  Update,
11
15
  UpsertPartialObject,
12
16
  UpsertResult,
17
+ WriteResult,
13
18
  } from "liwi-store";
14
19
  import type {
15
20
  Collection,
@@ -38,6 +43,20 @@ import type {
38
43
  MongoIndexSyncResult,
39
44
  SyncIndexesOptions,
40
45
  } from "./indexes/types.ts";
46
+ import { createReplacePipeline } from "./writes/createReplacePipeline.ts";
47
+ import { stampPartialUpdate } from "./writes/stampPartialUpdate.ts";
48
+
49
+ const resolveWriteTime = (options?: PartialUpdateOptions): Date | undefined =>
50
+ options?.touchUpdated === false ? undefined : new Date();
51
+
52
+ // the driver stores `undefined` as `null` (`ignoreUndefined` is off)
53
+ const toStoredValues = <T extends object>(values: T): T =>
54
+ Object.fromEntries(
55
+ Object.entries(values).map(([field, value]: [string, unknown]) => [
56
+ field,
57
+ value === undefined ? null : value,
58
+ ]),
59
+ ) as T;
41
60
 
42
61
  export type MongoUpsertResult<
43
62
  KeyValue extends AllowedKeyValue,
@@ -197,12 +216,42 @@ export default class MongoStore<
197
216
  return object as Model;
198
217
  }
199
218
 
219
+ /**
220
+ * Always sets `updated` to the write time and keeps the stored `created`: the
221
+ * object's own `created` and `updated` are ignored. The object is not mutated.
222
+ */
200
223
  async replaceOne(object: Model): Promise<Model> {
201
- if (!object.updated) object.updated = new Date();
224
+ const collection = await this.collection;
225
+ const next = await collection.findOneAndUpdate(
226
+ { _id: object._id } as Filter<Model>,
227
+ createReplacePipeline({ ...object, updated: new Date() }),
228
+ { returnDocument: "after" },
229
+ );
230
+ if (!next) {
231
+ throw new NotFoundError(`Document not found: ${String(object._id)}`);
232
+ }
233
+ return next as Model;
234
+ }
235
+
236
+ /** Same rules as `replaceOne`; `next` is derived from the atomic `prev`, no read back. */
237
+ async replaceOneWithInfo(
238
+ object: Model,
239
+ ): Promise<WriteResult<Model> | undefined> {
240
+ const replacement: Model = { ...object, updated: new Date() };
202
241
 
203
242
  const collection = await this.collection;
204
- await collection.replaceOne({ _id: object._id } as Filter<Model>, object);
205
- return object;
243
+ const prev = await collection.findOneAndUpdate(
244
+ { _id: object._id } as Filter<Model>,
245
+ createReplacePipeline(replacement),
246
+ { returnDocument: "before" },
247
+ );
248
+ if (!prev) return undefined;
249
+
250
+ const storedCreated = prev.created as Date | null | undefined;
251
+ return {
252
+ prev: prev as Model,
253
+ next: { ...replacement, created: storedCreated ?? replacement.created },
254
+ };
206
255
  }
207
256
 
208
257
  async upsertOne<
@@ -230,6 +279,12 @@ export default class MongoStore<
230
279
  object: UpsertPartialObject<MongoKeyPath, KeyValue, Model, K>,
231
280
  setOnInsertPartialObject?: Update<Model>["$setOnInsert"],
232
281
  ): Promise<MongoUpsertResult<KeyValue, Model>> {
282
+ if (setOnInsertPartialObject && "updated" in setOnInsertPartialObject) {
283
+ throw new ProtectedFieldUpdateError(
284
+ 'Cannot use $setOnInsert on "updated": upsert always sets it with $set, pass it in the object instead',
285
+ );
286
+ }
287
+
233
288
  const $setOnInsert: Update<Model>["$setOnInsert"] = {
234
289
  // @ts-expect-error -- created is Date as set in BaseModel
235
290
  created: object.created || new Date(),
@@ -260,7 +315,7 @@ export default class MongoStore<
260
315
  };
261
316
  }
262
317
 
263
- const next = { ...prev, ...$set };
318
+ const next = { ...prev, ...toStoredValues($set) };
264
319
 
265
320
  return {
266
321
  resolvedAs: "updated",
@@ -278,45 +333,86 @@ export default class MongoStore<
278
333
  key: KeyValue,
279
334
  partialUpdate: Update<Model>,
280
335
  criteria?: Criteria<Model>,
336
+ options?: PartialUpdateOptions,
281
337
  ): Promise<Model> {
338
+ const update = stampPartialUpdate(partialUpdate, resolveWriteTime(options));
282
339
  const collection = await this.collection;
283
- const commandResult = await collection.updateOne(
340
+ const next = await collection.findOneAndUpdate(
284
341
  { _id: key, ...criteria } as Filter<Model>,
285
- partialUpdate as UpdateFilter<Model>,
342
+ update as UpdateFilter<Model>,
343
+ { returnDocument: "after" },
286
344
  );
287
- if (!commandResult.acknowledged) {
288
- console.error(commandResult);
289
- throw new Error("Update failed");
290
- }
291
- const object = await this.findByKey(key);
292
- return object!;
345
+ if (!next) throw new NotFoundError(`Document not found: ${String(key)}`);
346
+ return next as Model;
347
+ }
348
+
349
+ /**
350
+ * `prev` is read atomically with the write. `next` is read right after it,
351
+ * so it may already include a later concurrent write, which emits its own change.
352
+ * Resolves `undefined` when no document matches.
353
+ */
354
+ async partialUpdateByKeyWithInfo(
355
+ key: KeyValue,
356
+ partialUpdate: Update<Model>,
357
+ criteria?: Criteria<Model>,
358
+ options?: PartialUpdateOptions,
359
+ ): Promise<PartialUpdateResult<Model> | undefined> {
360
+ const update = stampPartialUpdate(partialUpdate, resolveWriteTime(options));
361
+ const collection = await this.collection;
362
+ const prev = await collection.findOneAndUpdate(
363
+ { _id: key, ...criteria } as Filter<Model>,
364
+ update as UpdateFilter<Model>,
365
+ { returnDocument: "before" },
366
+ );
367
+ if (!prev) return undefined;
368
+
369
+ return { prev: prev as Model, next: await this.findByKey(key) };
293
370
  }
294
371
 
295
372
  partialUpdateOne(
296
373
  object: Model,
297
374
  partialUpdate: Update<Model>,
375
+ options?: PartialUpdateOptions,
298
376
  ): Promise<Model> {
299
- return this.partialUpdateByKey(object._id, partialUpdate);
377
+ return this.partialUpdateByKey(
378
+ object._id,
379
+ partialUpdate,
380
+ undefined,
381
+ options,
382
+ );
300
383
  }
301
384
 
302
- partialUpdateMany(
385
+ /** One `updateMany` command; every document gets the same `updated`. */
386
+ async partialUpdateMany(
303
387
  criteria: Criteria<Model>,
304
388
  partialUpdate: Update<Model>,
389
+ options?: PartialUpdateOptions,
305
390
  ): Promise<void> {
306
- return this.collection
307
- .then((collection) =>
308
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
309
- collection.updateMany(criteria as Filter<Model>, partialUpdate as any),
310
- )
311
- .then((res) => undefined); // TODO return updated object
391
+ const update = stampPartialUpdate(partialUpdate, resolveWriteTime(options));
392
+ const collection = await this.collection;
393
+ await collection.updateMany(
394
+ criteria as Filter<Model>,
395
+ update as UpdateFilter<Model>,
396
+ );
312
397
  }
313
398
 
314
- deleteByKey(key: KeyValue, criteria?: Criteria<Model>): Promise<void> {
315
- return this.collection
316
- .then((collection) =>
317
- collection.deleteOne({ _id: key, ...criteria } as Filter<Model>),
318
- )
319
- .then(() => undefined);
399
+ async deleteByKey(key: KeyValue, criteria?: Criteria<Model>): Promise<void> {
400
+ const collection = await this.collection;
401
+ await collection.deleteOne({ _id: key, ...criteria } as Filter<Model>);
402
+ }
403
+
404
+ async deleteByKeyWithInfo(
405
+ key: KeyValue,
406
+ criteria?: Criteria<Model>,
407
+ ): Promise<DeleteResult<Model> | undefined> {
408
+ const collection = await this.collection;
409
+ const prev = await collection.findOneAndDelete({
410
+ _id: key,
411
+ ...criteria,
412
+ } as Filter<Model>);
413
+ if (!prev) return undefined;
414
+
415
+ return { prev: prev as Model };
320
416
  }
321
417
 
322
418
  deleteOne(object: Model): Promise<void> {
@@ -1,8 +1,12 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { beforeEach, describe, it, mock } from "node:test";
3
+ import mongodb from "mongodb";
3
4
  import type { IndexDescription, IndexDescriptionInfo } from "mongodb";
4
5
  import type { MongoBaseModel } from "./MongoBaseModel.ts";
6
+ import MongoConnection from "./MongoConnection.ts";
7
+ import MongoRegistry from "./MongoRegistry.ts";
5
8
  import type { MongoStoreOptions } from "./MongoStore.ts";
9
+ import MongoStore from "./MongoStore.ts";
6
10
 
7
11
  interface TestModel extends MongoBaseModel {
8
12
  completed: boolean;
@@ -47,30 +51,12 @@ const db = {
47
51
  },
48
52
  };
49
53
 
50
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
51
- mock.module("mongodb", {
52
- // @ts-expect-error: not yet typed
53
- exports: {
54
- default: {
55
- MongoClient: {
56
- connect: () =>
57
- Promise.resolve({
58
- on: () => undefined,
59
- db: () => db,
60
- }),
61
- },
62
- ObjectId: class {
63
- toString(): string {
64
- return "generated-id";
65
- }
66
- },
67
- },
68
- },
69
- });
70
-
71
- const { default: MongoConnection } = await import("./MongoConnection.ts");
72
- const { default: MongoStore } = await import("./MongoStore.ts");
73
- const { default: MongoRegistry } = await import("./MongoRegistry.ts");
54
+ mock.method(mongodb.MongoClient, "connect", () =>
55
+ Promise.resolve({
56
+ on: () => undefined,
57
+ db: () => db,
58
+ } as unknown as mongodb.MongoClient),
59
+ );
74
60
 
75
61
  const createStore = (
76
62
  options?: MongoStoreOptions<TestModel>,