Repository: Automattic/mongoose (JavaScript)

Issue:
Connection autoCreate option behaves differently migrating 5 to 6
### Prerequisites

- [X] I have written a descriptive issue title
- [X] I have searched existing issues to ensure the bug has not already been reported


### Mongoose version

6.8.4

### Node.js version

18.13.0

### MongoDB server version

4.12.1 (driver)

### Typescript version (if applicable)

_No response_

### Description

When upgrading from Mongoose 5.13.15 to 6.8.4, we switched to explicitly setting the `autoCreate` option to `false` on the connection. We previously never set the autoCreate option on our Schema objects, so they should default to null (which means use the connection's autoCreate option). Our Schemas instantiate before the connection is created.

We found that in Mongoose 6, the autoCreate commands buffered prior to the connection being created were executed, issuing createCollection commands to the database. This didn't happen previously in Mongoose 5.

Workaround was to explicitly set autoCreate to false on the Schema objects.

### Steps to Reproduce

Added to `test/connection.test.js`

```
it('with autoCreate = false after schema create', async function() {
    const schema = new Schema({ name: String }, {
      collation: { locale: 'en_US', strength: 1 },
      collection: 'gh8814_Conn'
    });

    const Model = mongoose.model('gh8814_Conn', schema);

    const conn = mongoose.connect(start.uri, {
      autoCreate: false
    });

    await conn;
    await Model.init();

    const res = await mongoose.connection.db.listCollections().toArray();
    assert.ok(!res.map(c => c.name).includes('gh8814_Conn'));
  });
```

### Expected Behavior

Schema with `autoCreate: null` option set follows the connection's `autoCreate` option, even if schema is initialized first and the createCollection command is buffered.


Discussion:
Just to double check, is your assertion suppose to pass or fail?

```javascript
const mongoose = require('mongoose');

const testSchema = new mongoose.Schema({
  name: String
}, { collation: { locale: 'en_US', strength: 1 }, collection: 'gh8814_Conn' });

const Test = mongoose.model('gh8814_Conn', testSchema);

async function run() {
  await mongoose.connect('mongodb://localhost:27017', { autoCreate: false });
  await Test.init();
  const res = await mongoose.connection.db.listCollections().toArray();
  console.log(res);
  await mongoose.connection.dropDatabase();
};

run();
```
The assertion fails, and I think the code should be changed to make the test pass.

The gh8814_Conn collection shouldn't be created (following the `autoCreate` option on the connection).



Patch:
diff --git a/lib/model.js b/lib/model.js
index eebc957e3a4..a9f92b2f612 100644
--- a/lib/model.js
+++ b/lib/model.js
@@ -63,6 +63,7 @@ const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscrim
 const pushNestedArrayPaths = require('./helpers/model/pushNestedArrayPaths');
 const removeDeselectedForeignField = require('./helpers/populate/removeDeselectedForeignField');
 const setDottedPath = require('./helpers/path/setDottedPath');
+const STATES = require('./connectionstate');
 const util = require('util');
 const utils = require('./utils');
 
@@ -1352,17 +1353,30 @@ Model.init = function init(callback) {
   }
 
   const Promise = PromiseProvider.get();
-  const autoIndex = utils.getOption('autoIndex',
-    this.schema.options, this.db.config, this.db.base.options);
-  const autoCreate = utils.getOption('autoCreate',
-    this.schema.options, this.db.config, this.db.base.options);
-
-  const _ensureIndexes = autoIndex ?
-    cb => this.ensureIndexes({ _automatic: true }, cb) :
-    cb => cb();
-  const _createCollection = autoCreate ?
-    cb => this.createCollection({}, cb) :
-    cb => cb();
+
+  const model = this;
+  const _ensureIndexes = function(cb) {
+    const autoIndex = utils.getOption('autoIndex',
+      model.schema.options, model.db.config, model.db.base.options);
+    if (autoIndex) model.ensureIndexes({ _automatic: true }, cb);
+    else cb();
+  };
+  const _createCollection = function(cb) {
+    const conn = model.db;
+
+    if ((conn.readyState === STATES.connecting || conn.readyState === STATES.disconnected) && conn._shouldBufferCommands()) {
+      conn._queue.push({ fn: _createCollection, ctx: conn, args: [cb] });
+    } else {
+      try {
+        const autoCreate = utils.getOption('autoCreate',
+          model.schema.options, model.db.config, model.db.base.options);
+        if (autoCreate) model.createCollection({}, cb);
+        else cb();
+      } catch (err) {
+        return cb(err);
+      }
+    }
+  };
 
   this.$init = new Promise((resolve, reject) => {
     _createCollection(error => {


Test patch:
diff --git a/test/connection.test.js b/test/connection.test.js
index d27c7879605..71d44efb207 100644
--- a/test/connection.test.js
+++ b/test/connection.test.js
@@ -1526,4 +1526,23 @@ describe('connections:', function() {
     const connectionIds = m.connections.map(c => c.id);
     assert.deepEqual(connectionIds, [1, 2, 3, 4, 5]);
   });
+
+  it('with autoCreate = false after schema create (gh-12940)', async function() {
+    const m = new mongoose.Mongoose();
+
+    const schema = new Schema({ name: String }, {
+      collation: { locale: 'en_US', strength: 1 },
+      collection: 'gh12940_Conn'
+    });
+    const Model = m.model('gh12940_Conn', schema);
+
+    await m.connect(start.uri, {
+      autoCreate: false
+    });
+
+    await Model.init();
+
+    const res = await m.connection.db.listCollections().toArray();
+    assert.ok(!res.map(c => c.name).includes('gh12940_Conn'));
+  });
 });