|
const seed = Math.floor(Math.random() * 10000); |
|
const {dbName = "db-test", ...otherOpts} = opts; |
|
const url = `${getMongoUrl()}/${dbName}-${seed}`; |
### Information
- **Package:** `@tsed/testcontainers-mongo`
- **Version:** 8.18.0
- **Node:** 20.x
- **MongoDB image:** mongo:7.0.14 (also reproducible on 6.x)
### Description
`getMongoConnectionOptions()` derives the per-test database name from a random integer
in the range **0–9999**:
```js
// packages/orm/testcontainers-mongo/src/services/ContainerUtils.ts
export function getMongoConnectionOptions(id = "", opts = {}) {
const seed = Math.floor(Math.random() * 10000); // <-- only 10k possible names
const { dbName = "db-test", ...otherOpts } = opts;
const url = `${getMongoUrl()}/${dbName}-${seed}`;
...
}
Meanwhile startMongoServer() starts one container per process and caches it on
global[KEY], so every bootstrap() / create() in a run shares that single container:
// ContainerUtils.ts
const container = getEnvironment(KEY) || (await createMongoContainer(image));
And reset() (the documented teardown) does not clear data unless a single collection
name is explicitly passed:
// TestContainersMongo.ts
static async reset(collectionName) {
if (typeof collectionName === "string") {
await TestContainersMongo.cleanCollection(collectionName);
}
return PlatformTest.reset(); // only disconnects; data is left in place
}
Consequence: because the name space is only 10,000 and databases are never dropped or
emptied for the run, two bootstrap() calls eventually generate the same db-test-<n>.
A reused database still contains the previous suite's documents, so any test that seeds a
fixed/hard-coded _id fails on insert with:
MongoServerError: E11000 duplicate key error collection: db-test-<n>.<collection>
index: _id_ dup key: { _id: ObjectId('...') }
The probability grows with the number of suites in a run, so this shows up
as intermittent / flaky failures — and is far more likely in serial CI runs
(--no-file-parallelism, no Vitest retry) than in parallel local runs where a retry happens to
draw a different random name and masks the problem.
Expected behavior
Each test bootstrap should get an isolated database, and/or teardown should reliably clean
the data so a reused name can never leak documents into the next suite.
Suggested fixes (any one would resolve it)
- Guarantee uniqueness instead of low-entropy randomness. Use a monotonic counter, a
UUID/crypto suffix, or the worker id (e.g. process.env.VITEST_POOL_ID /
VITEST_WORKER_ID, JEST_WORKER_ID) so names cannot collide within a run:
const seed = `${process.pid}-${counter++}`; // or randomUUID()
- Make
reset() clean the whole database by default (drop or deleteMany({}) across all
collections), not only when a single collection name is supplied.
- At minimum, document that
reset() does not clear data and that DB names are random
with a tiny keyspace, so consumers know they must clean up themselves.
Reproduction
With the shared container, force two bootstraps to receive the same suffix (e.g. pin
Math.random), seed a document with a fixed _id in each, and run them sequentially — the
second insert throws E11000. Restoring unique names (or clearing collections on teardown)
makes it pass deterministically.
tsed/packages/testcontainers/mongo/src/services/ContainerUtils.ts
Lines 67 to 69 in db17b89
Meanwhile
startMongoServer()starts one container per process and caches it onglobal[KEY], so everybootstrap()/create()in a run shares that single container:And
reset()(the documented teardown) does not clear data unless a single collectionname is explicitly passed:
Consequence: because the name space is only 10,000 and databases are never dropped or
emptied for the run, two
bootstrap()calls eventually generate the samedb-test-<n>.A reused database still contains the previous suite's documents, so any test that seeds a
fixed/hard-coded
_idfails on insert with:The probability grows with the number of suites in a run, so this shows up
as intermittent / flaky failures — and is far more likely in serial CI runs
(
--no-file-parallelism, no Vitest retry) than in parallel local runs where a retry happens todraw a different random name and masks the problem.
Expected behavior
Each test bootstrap should get an isolated database, and/or teardown should reliably clean
the data so a reused name can never leak documents into the next suite.
Suggested fixes (any one would resolve it)
UUID/crypto suffix, or the worker id (e.g.
process.env.VITEST_POOL_ID/VITEST_WORKER_ID,JEST_WORKER_ID) so names cannot collide within a run:reset()clean the whole database by default (drop ordeleteMany({})across allcollections), not only when a single collection name is supplied.
reset()does not clear data and that DB names are randomwith a tiny keyspace, so consumers know they must clean up themselves.
Reproduction
With the shared container, force two bootstraps to receive the same suffix (e.g. pin
Math.random), seed a document with a fixed_idin each, and run them sequentially — thesecond insert throws
E11000. Restoring unique names (or clearing collections on teardown)makes it pass deterministically.