@reproapp/node-sdk 0.0.37 → 0.0.39

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.
@@ -0,0 +1,403 @@
1
+ const assert = require('assert');
2
+ const express = require('express');
3
+ const http = require('http');
4
+
5
+ process.env.REPRO_SDK_BACKGROUND_IDLE_DELAY_MS = '0';
6
+ process.env.REPRO_SDK_BACKGROUND_BUSY_IDLE_DELAY_MS = '0';
7
+
8
+ const {
9
+ reproMiddleware,
10
+ __reproTestHooks,
11
+ } = require('../dist');
12
+ const { flushIngestQueue } = require('../dist/ingest/client');
13
+
14
+ async function listen(server) {
15
+ await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
16
+ return server.address().port;
17
+ }
18
+
19
+ async function close(server) {
20
+ await new Promise((resolve, reject) => {
21
+ server.close((err) => (err ? reject(err) : resolve()));
22
+ });
23
+ }
24
+
25
+ async function request(port, path = '/untagged', options = {}) {
26
+ const method = options.method || 'POST';
27
+ const headers = {
28
+ ...(method === 'POST' ? { 'content-type': 'application/json' } : {}),
29
+ ...(options.headers || {}),
30
+ };
31
+ await new Promise((resolve, reject) => {
32
+ const req = http.request({
33
+ host: '127.0.0.1',
34
+ port,
35
+ path,
36
+ method,
37
+ headers,
38
+ }, (res) => {
39
+ res.resume();
40
+ res.on('end', resolve);
41
+ });
42
+ req.on('error', reject);
43
+ if (method === 'POST') {
44
+ req.end(JSON.stringify({ ok: true }));
45
+ } else {
46
+ req.end();
47
+ }
48
+ });
49
+ }
50
+
51
+ async function drainAll() {
52
+ await __reproTestHooks.drainSdkBackgroundQueueForTest();
53
+ await flushIngestQueue();
54
+ }
55
+
56
+ async function drainUntil(predicate, timeoutMs = 1000) {
57
+ const deadline = Date.now() + timeoutMs;
58
+ while (Date.now() <= deadline) {
59
+ await drainAll();
60
+ if (predicate()) return true;
61
+ await new Promise((resolve) => setTimeout(resolve, 10));
62
+ }
63
+ await drainAll();
64
+ return predicate();
65
+ }
66
+
67
+ function sleep(ms) {
68
+ return new Promise((resolve) => setTimeout(resolve, ms));
69
+ }
70
+
71
+ async function main() {
72
+ const posts = [];
73
+ const claimCounts = new Map();
74
+ const originalFetch = global.fetch;
75
+ global.fetch = async (url, init = {}) => {
76
+ const body = init.body ? JSON.parse(String(init.body)) : null;
77
+ posts.push({ url: String(url), body, headers: init.headers || {} });
78
+ if (String(url) === 'http://repro-api.test/api/v1/live-sessions/claim') {
79
+ const actor = String(body?.actorId || 'unknown');
80
+ const count = (claimCounts.get(actor) || 0) + 1;
81
+ claimCounts.set(actor, count);
82
+ const safeActor = actor.replace(/[^A-Za-z0-9_.:-]/g, '_');
83
+ const childSuffix = actor === 'actor-overlap' ? `_${count}` : '';
84
+ const childSessionId = `S_claim_child_${safeActor}${childSuffix}`;
85
+ return {
86
+ ok: true,
87
+ status: 200,
88
+ json: async () => ({
89
+ parentSessionId: `S_claim_parent_${safeActor}`,
90
+ childSessionId,
91
+ sessionId: childSessionId,
92
+ actionId: `A_claim_${safeActor}_${count}`,
93
+ clockOffsetMs: 0,
94
+ }),
95
+ text: async () => '{"ok":true}',
96
+ };
97
+ }
98
+ return {
99
+ ok: true,
100
+ status: 202,
101
+ json: async () => ({ ok: true }),
102
+ text: async () => '{"ok":true}',
103
+ };
104
+ };
105
+
106
+ __reproTestHooks.resetSdkBackgroundQueuesForTest();
107
+ __reproTestHooks.setBackendLiveTimingForTest({
108
+ segmentIdleMs: 1,
109
+ sessionMaxDurationMs: 20,
110
+ segmentMaxDurationMs: 20,
111
+ });
112
+
113
+ const cfg = {
114
+ appId: 'APP_backend_live',
115
+ tenantId: 'TENANT_backend_live',
116
+ appSecret: 'secret',
117
+ appName: 'Backend Live Test',
118
+ serviceName: 'Backend API',
119
+ apiBase: 'http://repro-api.test/api',
120
+ ingestBase: 'http://log-collector.test',
121
+ liveMode: {
122
+ enabled: true,
123
+ backendOnly: true,
124
+ defineActorId: ({ req }) => req.headers['x-test-actor'] || 'actor-1',
125
+ shouldCapture: ({ path }) => path !== '/skip',
126
+ actorType: 'user',
127
+ actorLabels: { tier: 'enterprise' },
128
+ },
129
+ };
130
+
131
+ const app = express();
132
+ app.use(express.json());
133
+ app.use(reproMiddleware(cfg));
134
+ app.post('/untagged', (_req, res) => {
135
+ res.json({ ok: true });
136
+ });
137
+ const server = http.createServer(app);
138
+ const port = await listen(server);
139
+
140
+ try {
141
+ await request(port, '/preflight', {
142
+ method: 'OPTIONS',
143
+ headers: {
144
+ origin: 'http://client.test',
145
+ 'access-control-request-method': 'POST',
146
+ 'access-control-request-headers': 'x-bug-session-id,x-bug-action-id,x-bug-request-start',
147
+ },
148
+ });
149
+ await drainAll();
150
+ assert.equal(
151
+ posts.length,
152
+ 0,
153
+ 'uncorrelated OPTIONS preflight should not create live sessions or ingest events',
154
+ );
155
+
156
+ await request(port, '/preflight', {
157
+ method: 'OPTIONS',
158
+ headers: {
159
+ 'x-bug-request-start': String(Date.now()),
160
+ },
161
+ });
162
+ await drainAll();
163
+ assert.equal(
164
+ posts.length,
165
+ 0,
166
+ 'OPTIONS with only Repro timing should not create backend-only live sessions',
167
+ );
168
+
169
+ const correlatedOptionsSid = 'S_frontend_options';
170
+ const correlatedOptionsAid = 'A_frontend_options';
171
+ await request(port, '/preflight', {
172
+ method: 'OPTIONS',
173
+ headers: {
174
+ 'x-bug-session-id': correlatedOptionsSid,
175
+ 'x-bug-action-id': correlatedOptionsAid,
176
+ 'x-bug-request-start': String(Date.now()),
177
+ },
178
+ });
179
+ await drainUntil(() => {
180
+ const events = posts
181
+ .filter((post) => post.url === 'http://log-collector.test/v1/ingest/events')
182
+ .flatMap((post) => Array.isArray(post.body?.events) ? post.body.events : []);
183
+ return events.some((event) =>
184
+ event.event_type === 'backend_request' &&
185
+ event.session_id === correlatedOptionsSid &&
186
+ event.payload?.action_id === correlatedOptionsAid
187
+ );
188
+ }, 2000);
189
+ assert(
190
+ posts
191
+ .filter((post) => post.url === 'http://log-collector.test/v1/ingest/events')
192
+ .flatMap((post) => Array.isArray(post.body?.events) ? post.body.events : [])
193
+ .some((event) =>
194
+ event.event_type === 'backend_request' &&
195
+ event.session_id === correlatedOptionsSid &&
196
+ event.payload?.action_id === correlatedOptionsAid
197
+ ),
198
+ 'correlated OPTIONS request should still be captured',
199
+ );
200
+ assert.equal(
201
+ posts.filter((post) => post.url === 'http://repro-api.test/api/v1/live-sessions').length,
202
+ 0,
203
+ 'frontend-correlated OPTIONS should not create a backend-only live parent',
204
+ );
205
+ assert.equal(
206
+ posts.filter((post) => post.url === 'http://repro-api.test/api/v1/live-sessions/claim').length,
207
+ 0,
208
+ 'frontend-correlated OPTIONS should not claim a backend-only live session',
209
+ );
210
+
211
+ posts.length = 0;
212
+ const correlatedPostSid = 'S_frontend_post';
213
+ const correlatedPostAid = 'A_frontend_post';
214
+ await request(port, '/untagged', {
215
+ headers: {
216
+ 'x-bug-session-id': correlatedPostSid,
217
+ 'x-bug-action-id': correlatedPostAid,
218
+ 'x-bug-request-start': String(Date.now()),
219
+ 'x-test-actor': 'backend-user-1',
220
+ },
221
+ });
222
+ await drainUntil(() => {
223
+ const events = posts
224
+ .filter((post) => post.url === 'http://log-collector.test/v1/ingest/events')
225
+ .flatMap((post) => Array.isArray(post.body?.events) ? post.body.events : []);
226
+ return events.some((event) =>
227
+ event.event_type === 'backend_event' &&
228
+ event.session_id === correlatedPostSid &&
229
+ event.payload?.sessionActor?.actorId === 'backend-user-1'
230
+ );
231
+ }, 2000);
232
+ const correlatedEvents = posts
233
+ .filter((post) => post.url === 'http://log-collector.test/v1/ingest/events')
234
+ .flatMap((post) => Array.isArray(post.body?.events) ? post.body.events : []);
235
+ assert(
236
+ correlatedEvents.some((event) =>
237
+ event.event_type === 'backend_event' &&
238
+ event.session_id === correlatedPostSid &&
239
+ event.actor_id === 'backend-user-1' &&
240
+ event.payload?.sessionActor?.actorId === 'backend-user-1'
241
+ ),
242
+ 'frontend-correlated request should emit backend actor alias evidence',
243
+ );
244
+ assert.equal(
245
+ posts.filter((post) => post.url === 'http://repro-api.test/api/v1/live-sessions').length,
246
+ 0,
247
+ 'frontend-correlated request should not create backend-only live sessions',
248
+ );
249
+ assert.equal(
250
+ posts.filter((post) => post.url === 'http://repro-api.test/api/v1/live-sessions/claim').length,
251
+ 0,
252
+ 'frontend-correlated request should not claim backend-only live sessions',
253
+ );
254
+
255
+ posts.length = 0;
256
+ await request(port);
257
+ await drainUntil(() => {
258
+ const claims = posts
259
+ .filter((post) => post.url === 'http://repro-api.test/api/v1/live-sessions/claim')
260
+ .map((post) => post.body);
261
+ const events = posts
262
+ .filter((post) => post.url === 'http://log-collector.test/v1/ingest/events')
263
+ .flatMap((post) => Array.isArray(post.body?.events) ? post.body.events : []);
264
+ return claims.length >= 1 &&
265
+ events.some((event) => event.event_type === 'backend_request') &&
266
+ events.some((event) => event.event_type === 'request_capture_manifest');
267
+ }, 2000);
268
+
269
+ const claims = posts
270
+ .filter((post) => post.url === 'http://repro-api.test/api/v1/live-sessions/claim')
271
+ .map((post) => post.body);
272
+ assert.equal(claims.length, 1, 'backend-only request should claim one central live session');
273
+ assert.equal(claims[0].captureMode, 'backend-only');
274
+ assert.equal(claims[0].actorId, 'actor-1');
275
+ assert.deepEqual(claims[0].actorIds, ['actor-1']);
276
+
277
+ const ingestEvents = posts
278
+ .filter((post) => post.url === 'http://log-collector.test/v1/ingest/events')
279
+ .flatMap((post) => Array.isArray(post.body?.events) ? post.body.events : []);
280
+ assert(
281
+ ingestEvents.some((event) =>
282
+ event.event_type === 'backend_request' &&
283
+ event.session_id === 'S_claim_child_actor-1' &&
284
+ event.actor_id === 'actor-1'
285
+ ),
286
+ 'backend request event should be captured for claimed child session and actor',
287
+ );
288
+ assert(
289
+ ingestEvents.some((event) =>
290
+ event.event_type === 'request_capture_manifest' &&
291
+ event.session_id === 'S_claim_child_actor-1'
292
+ ),
293
+ 'request capture manifest should be emitted for claimed backend session',
294
+ );
295
+ assert.equal(
296
+ posts.filter((post) => post.url === 'http://repro-api.test/api/v1/live-sessions').length,
297
+ 0,
298
+ 'SDK should not locally create backend-only sessions after central claim',
299
+ );
300
+ assert.equal(
301
+ posts.some((post) => String(post.url).includes('/finish')),
302
+ false,
303
+ 'SDK should not locally finish centrally claimed backend-only sessions',
304
+ );
305
+
306
+ console.log('backend live mode capture OK');
307
+ } finally {
308
+ await close(server);
309
+ }
310
+
311
+ posts.length = 0;
312
+ __reproTestHooks.resetSdkBackgroundQueuesForTest();
313
+ __reproTestHooks.setBackendLiveTimingForTest({
314
+ sessionMaxDurationMs: 30,
315
+ segmentMaxDurationMs: 30,
316
+ segmentIdleMs: 1,
317
+ });
318
+
319
+ const overlapCfg = {
320
+ ...cfg,
321
+ appId: 'APP_backend_live_overlap',
322
+ liveMode: {
323
+ ...cfg.liveMode,
324
+ defineActorId: () => 'actor-overlap',
325
+ shouldCapture: ({ path }) => path !== '/skip',
326
+ },
327
+ };
328
+
329
+ const overlapApp = express();
330
+ overlapApp.use(express.json());
331
+ overlapApp.use(reproMiddleware(overlapCfg));
332
+ overlapApp.post('/slow', async (_req, res) => {
333
+ await sleep(80);
334
+ res.json({ ok: true, kind: 'slow' });
335
+ });
336
+ overlapApp.post('/fast', (_req, res) => {
337
+ res.json({ ok: true, kind: 'fast' });
338
+ });
339
+ const overlapServer = http.createServer(overlapApp);
340
+ const overlapPort = await listen(overlapServer);
341
+
342
+ try {
343
+ const slowRequest = request(overlapPort, '/slow');
344
+ await sleep(50);
345
+ await request(overlapPort, '/fast');
346
+ await slowRequest;
347
+
348
+ await drainUntil(() => {
349
+ const claims = posts
350
+ .filter((post) => post.url === 'http://repro-api.test/api/v1/live-sessions/claim');
351
+ const events = posts
352
+ .filter((post) => post.url === 'http://log-collector.test/v1/ingest/events')
353
+ .flatMap((post) => Array.isArray(post.body?.events) ? post.body.events : []);
354
+ const requestSessionIds = new Set(
355
+ events
356
+ .filter((event) =>
357
+ event.event_type === 'backend_request' &&
358
+ event.actor_id === 'actor-overlap'
359
+ )
360
+ .map((event) => event.session_id),
361
+ );
362
+ return claims.length >= 2 && requestSessionIds.size >= 2;
363
+ });
364
+
365
+ const claims = posts
366
+ .filter((post) => post.url === 'http://repro-api.test/api/v1/live-sessions/claim')
367
+ .map((post) => post.body);
368
+ assert.equal(claims.length, 2, 'each backend-only request should ask the API for ownership');
369
+ assert(
370
+ claims.every((claim) => claim.actorId === 'actor-overlap'),
371
+ 'overlap claims should keep the actor identity',
372
+ );
373
+
374
+ const ingestEvents = posts
375
+ .filter((post) => post.url === 'http://log-collector.test/v1/ingest/events')
376
+ .flatMap((post) => Array.isArray(post.body?.events) ? post.body.events : []);
377
+ const requestSessionIds = new Set(
378
+ ingestEvents
379
+ .filter((event) =>
380
+ event.event_type === 'backend_request' &&
381
+ event.actor_id === 'actor-overlap'
382
+ )
383
+ .map((event) => event.session_id),
384
+ );
385
+ assert.deepEqual(
386
+ new Set(['S_claim_child_actor-overlap_1', 'S_claim_child_actor-overlap_2']),
387
+ requestSessionIds,
388
+ 'backend requests should use exactly the child sessions returned by the API claim',
389
+ );
390
+
391
+ console.log('backend live mode central claim overlap OK');
392
+ } finally {
393
+ await close(overlapServer);
394
+ global.fetch = originalFetch;
395
+ __reproTestHooks.resetSdkBackgroundQueuesForTest();
396
+ __reproTestHooks.setBackendLiveTimingForTest(null);
397
+ }
398
+ }
399
+
400
+ main().catch((err) => {
401
+ console.error(err);
402
+ process.exit(1);
403
+ });
@@ -133,12 +133,24 @@ function execPre(hooks, name, context) {
133
133
  });
134
134
  }
135
135
 
136
+ function execPost(hooks, name, context, args = []) {
137
+ return new Promise((resolve, reject) => {
138
+ hooks.execPost(name, context, args, {}, (error) => (error ? reject(error) : resolve()));
139
+ });
140
+ }
141
+
136
142
  function dbChanges(events) {
137
143
  return events
138
144
  .filter((event) => event.event_type === 'db_change')
139
145
  .flatMap((event) => Array.isArray(event.payload?.db) ? event.payload.db : []);
140
146
  }
141
147
 
148
+ function dbValueEntries(events) {
149
+ return events
150
+ .filter((event) => event.event_type === 'db_change')
151
+ .flatMap((event) => Array.isArray(event.payload?.dbValues) ? event.payload.dbValues : []);
152
+ }
153
+
142
154
  function requestCaptureManifests(events) {
143
155
  return events
144
156
  .filter((event) => event.event_type === 'request_capture_manifest')
@@ -146,6 +158,81 @@ function requestCaptureManifests(events) {
146
158
  .filter(Boolean);
147
159
  }
148
160
 
161
+ function makeNotificationModuleDocument() {
162
+ const notificationPreferences = Array.from({ length: 24 }, (_, index) => ({
163
+ trigger: 'SubjectScreened',
164
+ enabled: true,
165
+ channels: [
166
+ {
167
+ type: 'email',
168
+ template: {
169
+ subject: `Participant Screening ${index}`,
170
+ html: `<p>Screening notification ${index}</p>`,
171
+ },
172
+ recipients: {
173
+ to: ['noreply@korioclinical.com'],
174
+ cc: [
175
+ `pobrien+dev-conf-csm${index}@korioclinical.com`,
176
+ `mnebot+site-monitor${index}@korioclinical.com`,
177
+ ],
178
+ bcc: [`audit-${index}@korioclinical.com`],
179
+ },
180
+ },
181
+ ],
182
+ }));
183
+ const notificationTrigger = Array.from({ length: 24 }, (_, index) => ({
184
+ active: true,
185
+ event: 'SubjectScreened',
186
+ visitType: 'Screening',
187
+ subjectType: 'Participant',
188
+ rules: [
189
+ {
190
+ condition: {
191
+ path: 'subject.subjectStatusOption',
192
+ operator: 'equals',
193
+ value: 'Screened',
194
+ },
195
+ action: {
196
+ module: 'NotificationGeneratorService',
197
+ method: 'SubjectScreened',
198
+ order: index,
199
+ },
200
+ },
201
+ ],
202
+ }));
203
+
204
+ return {
205
+ __t: 'NotificationModule',
206
+ _id: '69e77c77ab541cfac917b37a',
207
+ active: true,
208
+ environment: 'dev-configure-pulmovant',
209
+ notificationPreferences,
210
+ notification_trigger: notificationTrigger,
211
+ protocol: {
212
+ id: '69e77c77ab541cfac917b36b',
213
+ name: 'RVT-2301-301',
214
+ },
215
+ schema_version: 1,
216
+ sponsor_name: 'Pulmovant',
217
+ study_name: 'RVT-2301-301',
218
+ };
219
+ }
220
+
221
+ class NotificationModuleHostDocument {
222
+ constructor(value) {
223
+ this.$__ = {};
224
+ this.value = value;
225
+ }
226
+
227
+ toObject() {
228
+ return JSON.parse(JSON.stringify(this.value));
229
+ }
230
+
231
+ toJSON() {
232
+ return this.toObject();
233
+ }
234
+ }
235
+
149
236
  async function testMongooseSuppressionDoesNotHideExplicitNativeInsertOne() {
150
237
  await withCapturedFetch(async (capturedBodies) => {
151
238
  __reproTestHooks.resetSdkBackgroundQueuesForTest();
@@ -477,6 +564,82 @@ async function testClientScreenNativeMongoInsertOneShapeIsCaptured() {
477
564
  });
478
565
  }
479
566
 
567
+ async function testLargeMongooseFindOneResultStoresFullResultMetaSidecar() {
568
+ await withCapturedFetch(async (capturedBodies) => {
569
+ __reproTestHooks.resetSdkBackgroundQueuesForTest();
570
+ const sessionId = 'S_mongoose_large_result_meta';
571
+ const actionId = 'A_mongoose_large_result_meta';
572
+ const cfg = {
573
+ ...makeConfig(sessionId, actionId),
574
+ privacy: { environment: 'dev', captureRaw: true },
575
+ };
576
+
577
+ const schema = new mongoose.Schema({}, { strict: false });
578
+ reproMongoosePlugin(cfg)(schema);
579
+
580
+ const notificationModule = makeNotificationModuleDocument();
581
+ const rawResult = new NotificationModuleHostDocument(notificationModule);
582
+ const queryContext = {
583
+ model: {
584
+ collection: {
585
+ name: 'configStudy',
586
+ },
587
+ },
588
+ getFilter: () => ({
589
+ __t: 'NotificationModule',
590
+ active: true,
591
+ protocol: {
592
+ id: '69e77c77ab541cfac917b36b',
593
+ },
594
+ }),
595
+ getUpdate: () => undefined,
596
+ projection: () => undefined,
597
+ getOptions: () => ({ lean: false }),
598
+ };
599
+
600
+ await runCapturedRequest(cfg, sessionId, actionId, async () => {
601
+ await execPre(schema.s.hooks, 'findOne', queryContext);
602
+ await execPost(schema.s.hooks, 'findOne', queryContext, [rawResult]);
603
+ });
604
+
605
+ await waitFor(() => dbChanges(flattenEvents(capturedBodies)).length === 1);
606
+ await waitFor(() => dbValueEntries(flattenEvents(capturedBodies)).some((entry) => entry.target === 'db.resultMeta'));
607
+ await drainSdk();
608
+
609
+ const [change] = dbChanges(flattenEvents(capturedBodies));
610
+ const values = dbValueEntries(flattenEvents(capturedBodies));
611
+ const resultMetaValue = values.find((entry) => entry.id === change.resultMetaValueCapture?.valueId);
612
+
613
+ assert.strictEqual(change.type, 'mongo');
614
+ assert.strictEqual(change.collection, 'configStudy');
615
+ assert.strictEqual(change.op, 'findOne');
616
+ assert.strictEqual(change.query.filter.__t, 'NotificationModule');
617
+ assert.strictEqual(change.resultMeta.docsCount, 1);
618
+ assert.deepStrictEqual(change.resultMeta.result.notificationPreferences[0], {
619
+ __kind: 'omitted',
620
+ reason: 'depth>3',
621
+ });
622
+ assert.deepStrictEqual(change.resultMeta.result.notification_trigger[0], {
623
+ __kind: 'omitted',
624
+ reason: 'depth>3',
625
+ });
626
+ assert.strictEqual(change.resultMetaValueCapture.status, 'stored');
627
+ assert.ok(change.resultMetaValueCapture.valueId, 'expected resultMeta value id');
628
+ assert.ok(resultMetaValue, 'expected matching db.resultMeta sidecar');
629
+ assert.strictEqual(resultMetaValue.target, 'db.resultMeta');
630
+ assert.strictEqual(resultMetaValue.value.__t, 'NotificationModule');
631
+ assert.strictEqual(resultMetaValue.value.notificationPreferences.length, 24);
632
+ assert.strictEqual(
633
+ resultMetaValue.value.notificationPreferences[0].channels[0].recipients.cc[0],
634
+ 'pobrien+dev-conf-csm0@korioclinical.com',
635
+ );
636
+ assert.strictEqual(
637
+ resultMetaValue.value.notification_trigger[0].rules[0].action.method,
638
+ 'SubjectScreened',
639
+ );
640
+ });
641
+ }
642
+
480
643
  async function testNativeInsertOneIsCaptured() {
481
644
  await withCapturedFetch(async (capturedBodies) => {
482
645
  __reproTestHooks.resetSdkBackgroundQueuesForTest();
@@ -742,6 +905,7 @@ async function testNativeOperationFailureIsCapturedAndRethrown() {
742
905
  await testReproMiddlewarePatchesPreloadedNativeMongoClientInsertOne();
743
906
  await testMongoClientDbCollectionChainIsPatchedWithoutCollectionExport();
744
907
  await testClientScreenNativeMongoInsertOneShapeIsCaptured();
908
+ await testLargeMongooseFindOneResultStoresFullResultMetaSidecar();
745
909
  await testNativeInsertOneIsCaptured();
746
910
  await testCaptureRawNativeInsertOneUsesJsonSafeTransportValues();
747
911
  await testNativeUpdateOneUpsertIsCaptured();