Skip to content

fix(update): preserve $setOnInsert.createdAt with overwriteImmutable: true#16278

Open
AbdelrahmanHafez wants to merge 5 commits into
masterfrom
fix/bulkwrite-per-op-timestamps-placement-gh-15781
Open

fix(update): preserve $setOnInsert.createdAt with overwriteImmutable: true#16278
AbdelrahmanHafez wants to merge 5 commits into
masterfrom
fix/bulkwrite-per-op-timestamps-placement-gh-15781

Conversation

@AbdelrahmanHafez
Copy link
Copy Markdown
Collaborator

@AbdelrahmanHafez AbdelrahmanHafez commented May 11, 2026

applyTimestampsToUpdate only checked top-level and $set for a user-provided createdAt, so bulkWrite upserts with $setOnInsert: { createdAt } and overwriteImmutable: true silently overwrote the value with now. Including $setOnInsert in the check preserves it.

Also cleans up #15781 tests added in #15819:

  • timestamps was placed outside the op descriptor in the loop tests, so all four [true, false, null, undefined] iterations ran with the default. Moved inside.
  • Loop tests now assert updatedAt actually varies with timestamps. Previously assertions were invariant, so a regression would still pass.
  • Removes a duplicate of the describe block.

The negative-case test in `describe('bulkWrite overwriteImmutable option
(gh-15781)')` placed `timestamps` as a sibling of `updateOne`/`updateMany`
instead of inside the op descriptor. Per-op `timestamps` is only honored
when nested inside the op (see `castBulkWrite.getTimestampsOpt`), so all
four loop iterations actually exercised the default path.

Moving `timestamps` inside the op descriptor surfaced that with
`timestamps: false`, the update became empty after immutable-field
stripping (`ssn` and the timestamps-plugin `createdAt` are both
immutable), causing `MongoInvalidArgumentError: Update document requires
atomic operators` from the driver. Added a mutable `name` field to the
update and asserted it changes, so all four iterations exercise genuine
immutable protection without producing an empty update.

Also remove a byte-identical duplicate of the `gh-15781` describe block.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a test regression around bulkWrite() per-operation timestamps handling (gh-15781) by ensuring the test inputs match how Mongoose reads timestamps for each bulk op.

Changes:

  • Move per-op timestamps into the updateOne / updateMany operation descriptors so the test exercises castBulkWrite.getTimestampsOpt correctly.
  • Add a mutable field (name) to the negative-case update payload to avoid the update becoming empty after immutable-path stripping.
  • Remove a duplicated describe block to avoid redundant coverage.

Comment thread test/model.updateOne.test.js
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Comment thread test/model.updateOne.test.js
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

test/model.updateOne.test.js:2849

  • The // Arrange comment is mis-indented (it’s aligned with the it( line instead of the function body), which violates the repo’s indent ESLint rule and may fail lint CI. Indent it to match the surrounding block (same level as the const { User } ... line).
      it(`can not update immutable fields without overwriteImmutable: true and timestamps: ${timestamps}`, async function() {
      // Arrange
        const { User } = createTestContext();

…erwriteImmutable: true`

`applyTimestampsToUpdate` only checked top-level and `$set` for a user-provided
`createdAt`, so a bulkWrite upsert with `$setOnInsert: { createdAt }` and
`overwriteImmutable: true` would silently overwrite the user's value with `now`.
@AbdelrahmanHafez AbdelrahmanHafez changed the title test(model): fix bulkWrite per-op timestamps placement (gh-15781) fix(update): preserve $setOnInsert.createdAt with overwriteImmutable: true May 11, 2026
@AbdelrahmanHafez AbdelrahmanHafez requested a review from Copilot May 11, 2026 20:33
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants