@reproapp/node-sdk 0.0.32 → 0.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -824,6 +824,19 @@ const getCtx = () => als.getStore() || {};
824
824
  function normalizeNativeMongoSuppressionOp(op) {
825
825
  return String(op ?? '').trim();
826
826
  }
827
+ const NATIVE_MONGO_WRITE_OPS = new Set([
828
+ 'insertOne',
829
+ 'insertMany',
830
+ 'updateOne',
831
+ 'updateMany',
832
+ 'replaceOne',
833
+ 'deleteOne',
834
+ 'deleteMany',
835
+ 'bulkWrite',
836
+ 'findOneAndUpdate',
837
+ 'findOneAndDelete',
838
+ 'findOneAndReplace',
839
+ ]);
827
840
  function pushNativeMongoSuppression(collection, ops) {
828
841
  const store = als.getStore();
829
842
  if (!store)
@@ -869,13 +882,28 @@ function releaseNativeMongoSuppression(target) {
869
882
  catch { }
870
883
  }
871
884
  }
885
+ function isMongooseInternalNativeMongoCall() {
886
+ let stack = '';
887
+ try {
888
+ stack = new Error().stack ?? '';
889
+ }
890
+ catch {
891
+ stack = '';
892
+ }
893
+ return stack.includes('/node_modules/mongoose/') ||
894
+ stack.includes('\\node_modules\\mongoose\\') ||
895
+ stack.includes('mongoose/lib/');
896
+ }
872
897
  function isNativeMongoSuppressed(collection, op) {
873
898
  const suppressions = getCtx().nativeMongoSuppressions;
874
899
  if (!Array.isArray(suppressions) || suppressions.length === 0)
875
900
  return false;
876
901
  const normalizedCollection = collection == null ? null : String(collection);
877
902
  const normalizedOp = normalizeNativeMongoSuppressionOp(op);
903
+ if (NATIVE_MONGO_WRITE_OPS.has(normalizedOp))
904
+ return false;
878
905
  const now = Date.now();
906
+ const matchingTokens = [];
879
907
  for (let i = suppressions.length - 1; i >= 0; i -= 1) {
880
908
  const token = suppressions[i];
881
909
  if (!token || now - token.startedAt > 60000) {
@@ -886,9 +914,17 @@ function isNativeMongoSuppressed(collection, op) {
886
914
  continue;
887
915
  if (token.ops && token.ops.size > 0 && !token.ops.has(normalizedOp))
888
916
  continue;
889
- return true;
917
+ matchingTokens.push(token);
890
918
  }
891
- return false;
919
+ if (matchingTokens.length === 0)
920
+ return false;
921
+ if (!isMongooseInternalNativeMongoCall()) {
922
+ return false;
923
+ }
924
+ for (const token of matchingTokens) {
925
+ popNativeMongoSuppression(token);
926
+ }
927
+ return true;
892
928
  }
893
929
  function currentClockSkewMs() {
894
930
  const store = als.getStore();
@@ -3878,7 +3914,7 @@ function estimateTraceBatchSerializedBytes(batch, requestRid, actionId, batchInd
3878
3914
  function chunkTraceEventsForTransport(events, requestRid, actionId) {
3879
3915
  if (!Array.isArray(events) || events.length === 0)
3880
3916
  return [];
3881
- const boundedEvents = events.slice(0, TRACE_MAX_EVENTS_PER_REQUEST);
3917
+ const boundedEvents = boundTraceEventsForTransport(events);
3882
3918
  const countBatches = chunkArray(boundedEvents, TRACE_BATCH_SIZE);
3883
3919
  const sizedBatches = [];
3884
3920
  let current = [];
@@ -3907,6 +3943,13 @@ function chunkTraceEventsForTransport(events, requestRid, actionId) {
3907
3943
  }
3908
3944
  return sizedBatches;
3909
3945
  }
3946
+ function boundTraceEventsForTransport(events) {
3947
+ if (events.length <= TRACE_MAX_EVENTS_PER_REQUEST)
3948
+ return events;
3949
+ const headCount = Math.floor(TRACE_MAX_EVENTS_PER_REQUEST / 2);
3950
+ const tailCount = TRACE_MAX_EVENTS_PER_REQUEST - headCount;
3951
+ return events.slice(0, headCount).concat(events.slice(events.length - tailCount));
3952
+ }
3910
3953
  function createCapturedValueEntry(params) {
3911
3954
  if (!__FULL_VALUE_CAPTURE_ENABLED)
3912
3955
  return undefined;
@@ -5154,6 +5197,9 @@ function emitDbQuery(cfg, sid, aid, payload) {
5154
5197
  const spanContext = payload?.spanContext ?? captureSpanContextFromTracer();
5155
5198
  if (!shouldCaptureDbSpan(spanContext))
5156
5199
  return;
5200
+ if (ctx.captureScope) {
5201
+ ctx.captureScope.dbChangeCount += 1;
5202
+ }
5157
5203
  const runCtx = {
5158
5204
  sid: ctx.sid,
5159
5205
  aid: ctx.aid,
@@ -5281,9 +5327,6 @@ async function emitDbQueryAsync(cfg, sid, aid, payload) {
5281
5327
  t: payload.t,
5282
5328
  }]
5283
5329
  });
5284
- if (enqueued && ctx.captureScope) {
5285
- ctx.captureScope.dbChangeCount += 1;
5286
- }
5287
5330
  }
5288
5331
  function buildMinimalUpdate(before, after) {
5289
5332
  const set = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reproapp/node-sdk",
3
- "version": "0.0.32",
3
+ "version": "0.0.34",
4
4
  "description": "Repro Nest SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -13,7 +13,7 @@
13
13
  "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
14
14
  "prepublishOnly": "npm run build",
15
15
  "test": "npm run build && node test/unawaited.test.js && node test/integration-unawaited.js && node test/request-flush-timing.test.js && node test/request-capture-background.test.js && node test/init-auto-http-server.test.js && node test/client-performance-regression.test.js && node test/request-manifest-priority.test.js && node test/runtime-no-double-invoke.test.js && node test/express-trace-http-args.test.js && node test/trace-batch-size.test.js && node test/sdk-background-priority.test.js && node test/ingest-queue-retention.test.js && node test/ingest-high-priority-retention.test.js && node test/ingest-critical-retention.test.js && node test/ingest-critical-retry-retention.test.js && node test/ingest-timeout.test.js && node -r ./tracer/register test/promise-map.test.js && node test/disable-subtree.test.js && node test/circular-capture.test.js && node test/wrap-plugin-arrow-args.test.js && node test/wrap-plugin-super-call.test.js && node test/function-arg-identity.test.js && node test/privacy-runtime-policy.test.js && node test/runtime-privacy-materialization.test.js && node test/kafka-runtime-privacy-policy.test.js && node test/amqplib-integration.test.js && node test/mongodb-integration.test.js && node test/mongodb-esm-import-hook.test.js && node test/mongodb-suppression-cleanup.test.js",
16
- "test:containers": "npm run build && node test/runtime-no-double-invoke.testcontainers.test.js && node test/wrap-plugin-super-call.testcontainers.test.js && node test/function-arg-identity.testcontainers.test.js"
16
+ "test:containers": "npm run build && node test/runtime-no-double-invoke.testcontainers.test.js && node test/wrap-plugin-super-call.testcontainers.test.js && node test/function-arg-identity.testcontainers.test.js && node test/mongodb-mongoose-native-suppression.testcontainers.test.js"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "express": "^5.1.0",
package/src/index.ts CHANGED
@@ -1227,6 +1227,20 @@ function normalizeNativeMongoSuppressionOp(op: string | null | undefined): strin
1227
1227
  return String(op ?? '').trim();
1228
1228
  }
1229
1229
 
1230
+ const NATIVE_MONGO_WRITE_OPS = new Set([
1231
+ 'insertOne',
1232
+ 'insertMany',
1233
+ 'updateOne',
1234
+ 'updateMany',
1235
+ 'replaceOne',
1236
+ 'deleteOne',
1237
+ 'deleteMany',
1238
+ 'bulkWrite',
1239
+ 'findOneAndUpdate',
1240
+ 'findOneAndDelete',
1241
+ 'findOneAndReplace',
1242
+ ]);
1243
+
1230
1244
  function pushNativeMongoSuppression(collection: any, ops?: string[] | readonly string[]): NativeMongoSuppression | null {
1231
1245
  const store = als.getStore();
1232
1246
  if (!store) return null;
@@ -1267,12 +1281,26 @@ function releaseNativeMongoSuppression(target: any): void {
1267
1281
  }
1268
1282
  }
1269
1283
 
1284
+ function isMongooseInternalNativeMongoCall(): boolean {
1285
+ let stack = '';
1286
+ try {
1287
+ stack = new Error().stack ?? '';
1288
+ } catch {
1289
+ stack = '';
1290
+ }
1291
+ return stack.includes('/node_modules/mongoose/') ||
1292
+ stack.includes('\\node_modules\\mongoose\\') ||
1293
+ stack.includes('mongoose/lib/');
1294
+ }
1295
+
1270
1296
  function isNativeMongoSuppressed(collection: any, op: string): boolean {
1271
1297
  const suppressions = (getCtx() as Ctx).nativeMongoSuppressions;
1272
1298
  if (!Array.isArray(suppressions) || suppressions.length === 0) return false;
1273
1299
  const normalizedCollection = collection == null ? null : String(collection);
1274
1300
  const normalizedOp = normalizeNativeMongoSuppressionOp(op);
1301
+ if (NATIVE_MONGO_WRITE_OPS.has(normalizedOp)) return false;
1275
1302
  const now = Date.now();
1303
+ const matchingTokens: NativeMongoSuppression[] = [];
1276
1304
  for (let i = suppressions.length - 1; i >= 0; i -= 1) {
1277
1305
  const token = suppressions[i];
1278
1306
  if (!token || now - token.startedAt > 60000) {
@@ -1281,9 +1309,18 @@ function isNativeMongoSuppressed(collection: any, op: string): boolean {
1281
1309
  }
1282
1310
  if (token.collection && normalizedCollection && token.collection !== normalizedCollection) continue;
1283
1311
  if (token.ops && token.ops.size > 0 && !token.ops.has(normalizedOp)) continue;
1284
- return true;
1312
+ matchingTokens.push(token);
1285
1313
  }
1286
- return false;
1314
+ if (matchingTokens.length === 0) return false;
1315
+
1316
+ if (!isMongooseInternalNativeMongoCall()) {
1317
+ return false;
1318
+ }
1319
+
1320
+ for (const token of matchingTokens) {
1321
+ popNativeMongoSuppression(token);
1322
+ }
1323
+ return true;
1287
1324
  }
1288
1325
 
1289
1326
  function currentClockSkewMs(): number {
@@ -4605,7 +4642,7 @@ function chunkTraceEventsForTransport(
4605
4642
  ): TraceEventRecord[][] {
4606
4643
  if (!Array.isArray(events) || events.length === 0) return [];
4607
4644
 
4608
- const boundedEvents = events.slice(0, TRACE_MAX_EVENTS_PER_REQUEST);
4645
+ const boundedEvents = boundTraceEventsForTransport(events);
4609
4646
  const countBatches = chunkArray(boundedEvents, TRACE_BATCH_SIZE);
4610
4647
  const sizedBatches: TraceEventRecord[][] = [];
4611
4648
  let current: TraceEventRecord[] = [];
@@ -4649,6 +4686,13 @@ function chunkTraceEventsForTransport(
4649
4686
  return sizedBatches;
4650
4687
  }
4651
4688
 
4689
+ function boundTraceEventsForTransport(events: TraceEventRecord[]): TraceEventRecord[] {
4690
+ if (events.length <= TRACE_MAX_EVENTS_PER_REQUEST) return events;
4691
+ const headCount = Math.floor(TRACE_MAX_EVENTS_PER_REQUEST / 2);
4692
+ const tailCount = TRACE_MAX_EVENTS_PER_REQUEST - headCount;
4693
+ return events.slice(0, headCount).concat(events.slice(events.length - tailCount));
4694
+ }
4695
+
4652
4696
  function createCapturedValueEntry(
4653
4697
  params: {
4654
4698
  target:
@@ -6110,6 +6154,9 @@ function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
6110
6154
  const ctx = getCtx() as Ctx;
6111
6155
  const spanContext = payload?.spanContext ?? captureSpanContextFromTracer();
6112
6156
  if (!shouldCaptureDbSpan(spanContext)) return;
6157
+ if (ctx.captureScope) {
6158
+ ctx.captureScope.dbChangeCount += 1;
6159
+ }
6113
6160
  const runCtx: Ctx = {
6114
6161
  sid: ctx.sid,
6115
6162
  aid: ctx.aid,
@@ -6236,9 +6283,6 @@ async function emitDbQueryAsync(cfg: any, sid?: string, aid?: string, payload?:
6236
6283
  t: payload.t,
6237
6284
  }]
6238
6285
  });
6239
- if (enqueued && ctx.captureScope) {
6240
- ctx.captureScope.dbChangeCount += 1;
6241
- }
6242
6286
  }
6243
6287
 
6244
6288
  function buildMinimalUpdate(before: any, after: any) {
@@ -8,6 +8,7 @@ process.env.TRACE_LINGER_AFTER_FINISH_MS = '0';
8
8
  const assert = require('assert');
9
9
  const { EventEmitter } = require('events');
10
10
  const mongodb = require('mongodb');
11
+ const mongoose = require('mongoose');
11
12
 
12
13
  const originalNativeMongoInsertOne = mongodb.Collection.prototype.insertOne;
13
14
 
@@ -18,7 +19,7 @@ mongodb.Collection.prototype.insertOne = async function reproFakeNativeMongoInse
18
19
  };
19
20
 
20
21
  const { flushIngestQueue } = require('../dist/ingest/client');
21
- const { reproMiddleware, __reproTestHooks } = require('../dist');
22
+ const { reproMiddleware, reproMongoosePlugin, __reproTestHooks } = require('../dist');
22
23
 
23
24
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
24
25
 
@@ -126,12 +127,90 @@ async function runCapturedRequest(cfg, sessionId, actionId, fn) {
126
127
  res.end();
127
128
  }
128
129
 
130
+ function execPre(hooks, name, context) {
131
+ return new Promise((resolve, reject) => {
132
+ hooks.execPre(name, context, [], (error) => (error ? reject(error) : resolve()));
133
+ });
134
+ }
135
+
129
136
  function dbChanges(events) {
130
137
  return events
131
138
  .filter((event) => event.event_type === 'db_change')
132
139
  .flatMap((event) => Array.isArray(event.payload?.db) ? event.payload.db : []);
133
140
  }
134
141
 
142
+ function requestCaptureManifests(events) {
143
+ return events
144
+ .filter((event) => event.event_type === 'request_capture_manifest')
145
+ .map((event) => event.payload?.requestCaptureManifest ?? event.payload)
146
+ .filter(Boolean);
147
+ }
148
+
149
+ async function testMongooseSuppressionDoesNotHideExplicitNativeInsertOne() {
150
+ await withCapturedFetch(async (capturedBodies) => {
151
+ __reproTestHooks.resetSdkBackgroundQueuesForTest();
152
+ const sessionId = 'S_mongo_mixed_driver_insert';
153
+ const actionId = 'A_mongo_mixed_driver_insert';
154
+ const cfg = makeConfig(sessionId, actionId);
155
+
156
+ const schema = new mongoose.Schema({ subjectNumber: String, status: String });
157
+ reproMongoosePlugin(cfg)(schema);
158
+ const Model = mongoose.model(`ReproMixedNativeMongo${Date.now()}`, schema, 'subjects');
159
+ const pendingMongooseDoc = new Model({ subjectNumber: 'pre-save', status: 'pending' });
160
+
161
+ class FakeCollection {
162
+ constructor() {
163
+ this.collectionName = 'subjects';
164
+ this.dbName = 'clinical';
165
+ }
166
+
167
+ async insertOne(doc, options) {
168
+ assert.strictEqual(options.session.id, 'native-explicit-session-1');
169
+ doc._id = doc._id ?? 'subject-explicit-1';
170
+ return { acknowledged: true, insertedId: doc._id };
171
+ }
172
+ }
173
+
174
+ __reproTestHooks.patchMongoCollectionPrototypeForTest(FakeCollection.prototype, cfg);
175
+ const subjectsCollection = new FakeCollection();
176
+
177
+ await runCapturedRequest(cfg, sessionId, actionId, async () => {
178
+ await execPre(schema.s.hooks, 'save', pendingMongooseDoc);
179
+ assert.strictEqual(__reproTestHooks.getNativeMongoSuppressionCountForTest(), 1);
180
+
181
+ const result = await subjectsCollection.insertOne(
182
+ { subjectNumber: '10013038', status: 'screened' },
183
+ {
184
+ session: {
185
+ id: 'native-explicit-session-1',
186
+ hasEnded: false,
187
+ inTransaction: () => true,
188
+ },
189
+ },
190
+ );
191
+ assert.deepStrictEqual(result, { acknowledged: true, insertedId: 'subject-explicit-1' });
192
+ });
193
+
194
+ await waitFor(() => dbChanges(flattenEvents(capturedBodies)).length === 1);
195
+ await waitFor(() => requestCaptureManifests(flattenEvents(capturedBodies)).length === 1);
196
+ await drainSdk();
197
+
198
+ const [change] = dbChanges(flattenEvents(capturedBodies));
199
+ const [manifest] = requestCaptureManifests(flattenEvents(capturedBodies));
200
+ assert.strictEqual(change.type, 'mongo');
201
+ assert.strictEqual(change.collection, 'subjects');
202
+ assert.strictEqual(change.op, 'insertOne');
203
+ assert.strictEqual(change.pk._id, 'subject-explicit-1');
204
+ assert.strictEqual(change.query.driver, 'mongodb');
205
+ assert.strictEqual(change.query.db, 'clinical');
206
+ assert.strictEqual(change.query.doc.subjectNumber, '10013038');
207
+ assert.strictEqual(change.query.options.session.__kind, 'mongodb_client_session');
208
+ assert.strictEqual(change.query.options.session.id, 'native-explicit-session-1');
209
+ assert.strictEqual(change.after.subjectNumber, '10013038');
210
+ assert.strictEqual(manifest.expected.dbChange, 1);
211
+ });
212
+ }
213
+
135
214
  async function testReproMiddlewarePatchesPreloadedNativeMongoClientInsertOne() {
136
215
  await withCapturedFetch(async (capturedBodies) => {
137
216
  __reproTestHooks.resetSdkBackgroundQueuesForTest();
@@ -452,6 +531,7 @@ async function testNativeOperationFailureIsCapturedAndRethrown() {
452
531
  }
453
532
 
454
533
  (async () => {
534
+ await testMongooseSuppressionDoesNotHideExplicitNativeInsertOne();
455
535
  await testReproMiddlewarePatchesPreloadedNativeMongoClientInsertOne();
456
536
  await testMongoClientDbCollectionChainIsPatchedWithoutCollectionExport();
457
537
  await testNativeInsertOneIsCaptured();
@@ -0,0 +1,178 @@
1
+ process.env.REPRO_INGEST_REQUEST_TIMEOUT_MS = '50';
2
+ process.env.REPRO_SDK_BACKGROUND_MAX_DEFER_MS = '25';
3
+ process.env.REPRO_SDK_BACKGROUND_IDLE_DELAY_MS = '0';
4
+ process.env.REPRO_SDK_BACKGROUND_BUSY_IDLE_DELAY_MS = '0';
5
+ process.env.TRACE_IDLE_FLUSH_MS = '10';
6
+ process.env.TRACE_LINGER_AFTER_FINISH_MS = '0';
7
+
8
+ const assert = require('assert');
9
+ const { EventEmitter } = require('events');
10
+ const mongoose = require('mongoose');
11
+ const { GenericContainer, Wait } = require('testcontainers');
12
+
13
+ const { flushIngestQueue } = require('../dist/ingest/client');
14
+ const { reproMiddleware, reproMongoosePlugin, __reproTestHooks } = require('../dist');
15
+
16
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
17
+
18
+ async function waitFor(predicate, timeoutMs = 5000) {
19
+ const deadline = Date.now() + timeoutMs;
20
+ while (Date.now() < deadline) {
21
+ if (predicate()) return;
22
+ await sleep(25);
23
+ }
24
+ assert(predicate(), 'timed out waiting for expected ingest payload');
25
+ }
26
+
27
+ function flattenEvents(posts) {
28
+ return posts.flatMap((body) => Array.isArray(body?.events) ? body.events : []);
29
+ }
30
+
31
+ function dbChanges(events) {
32
+ return events
33
+ .filter((event) => event.event_type === 'db_change')
34
+ .flatMap((event) => Array.isArray(event.payload?.db) ? event.payload.db : []);
35
+ }
36
+
37
+ function requestCaptureManifests(events) {
38
+ return events
39
+ .filter((event) => event.event_type === 'request_capture_manifest')
40
+ .map((event) => event.payload?.requestCaptureManifest ?? event.payload)
41
+ .filter(Boolean);
42
+ }
43
+
44
+ function makeReqRes(sessionId, actionId) {
45
+ const req = new EventEmitter();
46
+ req.method = 'POST';
47
+ req.url = '/subjects';
48
+ req.originalUrl = '/subjects';
49
+ req.headers = {
50
+ 'x-bug-session-id': sessionId,
51
+ 'x-bug-action-id': actionId,
52
+ };
53
+ req.query = {};
54
+ req.params = {};
55
+
56
+ const res = new EventEmitter();
57
+ res.statusCode = 200;
58
+ res.getHeader = () => undefined;
59
+ res.getHeaders = () => ({});
60
+ res.write = () => true;
61
+ res.end = function end() {
62
+ this.emit('finish');
63
+ return this;
64
+ };
65
+
66
+ return { req, res };
67
+ }
68
+
69
+ async function runCapturedRequest(cfg, sessionId, actionId, fn) {
70
+ const middleware = reproMiddleware(cfg);
71
+ const { req, res } = makeReqRes(sessionId, actionId);
72
+ await new Promise((resolve, reject) => {
73
+ middleware(req, res, (err) => {
74
+ if (err) {
75
+ reject(err);
76
+ return;
77
+ }
78
+ Promise.resolve().then(fn).then(resolve, reject);
79
+ });
80
+ });
81
+ res.end();
82
+ }
83
+
84
+ async function startMongo() {
85
+ if (process.env.REPRO_MONGO_TEST_URI) {
86
+ return {
87
+ uri: process.env.REPRO_MONGO_TEST_URI,
88
+ stop: async () => {},
89
+ };
90
+ }
91
+
92
+ const container = await new GenericContainer('mongo:7')
93
+ .withExposedPorts(27017)
94
+ .withTmpFs({ '/data/db': 'rw,noexec,nosuid,size=1g' })
95
+ .withCommand(['mongod', '--bind_ip_all', '--setParameter', 'diagnosticDataCollectionEnabled=false'])
96
+ .withWaitStrategy(Wait.forListeningPorts())
97
+ .start();
98
+ return {
99
+ uri: `mongodb://${container.getHost()}:${container.getMappedPort(27017)}/repro_mongoose_native_suppression`,
100
+ stop: () => container.stop(),
101
+ };
102
+ }
103
+
104
+ async function main() {
105
+ const capturedBodies = [];
106
+ const originalFetch = global.fetch;
107
+ global.fetch = async (_url, init) => {
108
+ capturedBodies.push(JSON.parse(String(init?.body ?? '{}')));
109
+ return {
110
+ ok: true,
111
+ status: 202,
112
+ json: async () => ({ ok: true }),
113
+ text: async () => '{"ok":true}',
114
+ };
115
+ };
116
+
117
+ let container;
118
+ let connection;
119
+ try {
120
+ __reproTestHooks.resetSdkBackgroundQueuesForTest();
121
+ container = await startMongo();
122
+ connection = await mongoose.createConnection(container.uri).asPromise();
123
+
124
+ const cfg = {
125
+ tenantId: 'TENANT_test',
126
+ appId: 'APP_test',
127
+ appSecret: 'secret',
128
+ captureHeaders: false,
129
+ privacy: { environment: 'dev', captureRaw: true },
130
+ ingestBase: 'http://127.0.0.1:65535',
131
+ };
132
+
133
+ const schema = new mongoose.Schema({ subjectNumber: String, status: String });
134
+ reproMongoosePlugin(cfg)(schema);
135
+ const Subject = connection.model('Subject', schema, 'subjects');
136
+
137
+ await runCapturedRequest(cfg, 'S_mongoose_native_suppression', 'A_mongoose_native_suppression', async () => {
138
+ await Subject.create({ subjectNumber: '10013038', status: 'screened' });
139
+ });
140
+
141
+ await waitFor(() => dbChanges(flattenEvents(capturedBodies)).length >= 2);
142
+ await waitFor(() => requestCaptureManifests(flattenEvents(capturedBodies)).length === 1);
143
+ await __reproTestHooks.drainSdkBackgroundQueueForTest();
144
+ await flushIngestQueue();
145
+
146
+ const changes = dbChanges(flattenEvents(capturedBodies));
147
+ const manifest = requestCaptureManifests(flattenEvents(capturedBodies))[0];
148
+ const mongooseWrite = changes.find((change) => (
149
+ change.type === 'mongo' &&
150
+ change.collection === 'subjects' &&
151
+ change.op === 'insert' &&
152
+ change.after?.subjectNumber === '10013038'
153
+ ));
154
+ const nativeWrite = changes.find((change) => (
155
+ change.type === 'mongo' &&
156
+ change.collection === 'subjects' &&
157
+ change.op === 'insertOne' &&
158
+ change.query?.driver === 'mongodb' &&
159
+ change.after?.subjectNumber === '10013038'
160
+ ));
161
+ assert.ok(mongooseWrite, 'expected mongoose-level insert evidence');
162
+ assert.ok(nativeWrite, 'expected native mongodb insertOne evidence');
163
+ assert.ok(manifest.expected.dbChange >= 2);
164
+
165
+ console.log('mongodb mongoose native suppression testcontainers OK');
166
+ } finally {
167
+ await __reproTestHooks.drainSdkBackgroundQueueForTest().catch(() => {});
168
+ await flushIngestQueue().catch(() => {});
169
+ global.fetch = originalFetch;
170
+ if (connection) await connection.close().catch(() => {});
171
+ if (container) await container.stop().catch(() => {});
172
+ }
173
+ }
174
+
175
+ main().catch((error) => {
176
+ console.error(error);
177
+ process.exit(1);
178
+ });
@@ -66,6 +66,11 @@ function main() {
66
66
  cappedEventCount <= 1000,
67
67
  `expected trace transport to cap events per request, got ${cappedEventCount}`,
68
68
  );
69
+ const cappedEvents = cappedBatches.flat();
70
+ assert.strictEqual(cappedEvents[0].fn, 'fn0');
71
+ assert.strictEqual(cappedEvents[499].fn, 'fn499');
72
+ assert.strictEqual(cappedEvents[500].fn, 'fn1000');
73
+ assert.strictEqual(cappedEvents[cappedEvents.length - 1].fn, 'fn1499');
69
74
 
70
75
  // eslint-disable-next-line no-console
71
76
  console.log('trace batch size chunking OK');