Skip to content

Commit

Permalink
feat: Reimplement mongo-prebuild. Change config options.
Browse files Browse the repository at this point in the history
BREAKING CHANGES
  • Loading branch information
nodkz committed Jun 7, 2017
1 parent d874747 commit 0bd048a
Show file tree
Hide file tree
Showing 14 changed files with 629 additions and 227 deletions.
5 changes: 5 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
"jasmine": true,
"jest": true
},
"globals": {
"Class": true,
"Iterator": true,
"$Shape": true,
},
"plugins": [
"flowtype",
"prettier"
Expand Down
36 changes: 34 additions & 2 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -1,11 +1,43 @@
[ignore]
.*/lib/.*
.*/src/.*
.*/coverage/.*
<PROJECT_ROOT>/lib/.*
<PROJECT_ROOT>/dist/.*
.*/node_modules/ajv.*
.*/node_modules/acorn.*
.*/node_modules/async.*
.*/node_modules/babel.*
.*/node_modules/bluebird.*
.*/node_modules/caniuse-db.*
.*/node_modules/config-chain.*
.*/node_modules/conventional-changelog.*
.*/node_modules/core-js.*
.*/node_modules/cssstyle.*
.*/node_modules/diff.*
.*/node_modules/es5-ext.*
.*/node_modules/escope.*
.*/node_modules/escodegen.*
.*/node_modules/eslint.*
.*/node_modules/github.*
.*/node_modules/fsevents.*
.*/node_modules/jsdoctypeparser.*
.*/node_modules/jsdom.*
.*/node_modules/iconv.*
.*/node_modules/istanbul.*
.*/node_modules/handlebars.*
.*/node_modules/markdown.*
.*/node_modules/node-notifier.*
.*/node_modules/npmconf.*
.*/node_modules/prettier.*
.*/node_modules/source-map.*
.*/node_modules/travis.*
.*/node_modules/uglify.*
.*/node_modules/yargs.*

[include]

[libs]

[options]
esproposal.class_instance_fields=enable
suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe
unsafe.enable_getters_and_setters=true
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,29 @@ mongod.stop();
// or it will be stopped automatically when you exit from script
```

### Available options
All options are optional.
```js
const mongod = new MongodbMemoryServer({
instance: {
port?: ?number, // by default choose any free port
dbPath?: string, // by default create in temp directory
storageEngine?: string, // by default `ephemeralForTest`
debug?: boolean, // by default false
},
binary: {
version?: string, // by default '3.4.4'
downloadDir?: string, // by default %HOME/.mongodb-prebuilt
platform?: string, // by default os.platform()
arch?: string, // by default os.arch()
http?: any, // see mongodb-download package
debug?: boolean, // by default false
},
debug?: boolean, // by default false
autoStart?: boolean, // by default true
});
```
### Provide connection string to mongoose
```js
import mongoose from 'mongoose';
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@
"semantic-release": "^6.3.6"
},
"dependencies": {
"debug": "^2.6.8",
"get-port": "^3.1.0",
"mongodb-prebuilt": "^6.3.3",
"glob": "^7.1.2",
"mongodb-download": "^2.2.3",
"tmp": "^0.0.31",
"uuid": "^3.0.1"
},
Expand Down
192 changes: 192 additions & 0 deletions src/MongoMemoryServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/* @flow */

import type { ChildProcess } from 'child_process';
import uuid from 'uuid/v4';
import tmp from 'tmp';
import getport from 'get-port';
import MongoInstance from './util/MongoInstance';

tmp.setGracefulCleanup();

export type MongoMemoryServerOptsT = {
instance: {
port?: ?number,
dbPath?: string,
storageEngine?: string,
debug?: boolean,
},
binary: {
version?: string,
downloadDir?: string,
platform?: string,
arch?: string,
http?: any,
debug?: boolean,
},
debug?: boolean,
spawn: any,
autoStart?: boolean,
};

export type MongoInstanceDataT = {
port: number,
dbPath: string,
uri: string,
storageEngine: string,
mongod: ChildProcess,
tmpDir?: {
name: string,
removeCallback: Function,
},
};

async function generateConnectionString(port: number, dbName: ?string): Promise<string> {
const db = dbName || (await uuid());
return `mongodb://localhost:${port}/${db}`;
}

export default class MongoMemoryServer {
isRunning: boolean = false;
runningInstance: ?Promise<MongoInstanceDataT>;
opts: MongoMemoryServerOptsT;

constructor(opts?: $Shape<MongoMemoryServerOptsT> = {}) {
this.opts = opts;
if (!this.opts.instance) this.opts.instance = {};
if (!this.opts.binary) this.opts.binary = {};

// autoStart by default
if (!opts.hasOwnProperty('autoStart') || opts.autoStart) {
if (opts.debug) {
console.log('Autostarting MongoDB instance...');
}
this.start();
}
}

debug(msg: string) {
if (this.opts.debug) {
console.log(msg);
}
}

async start(): Promise<boolean> {
if (this.runningInstance) {
throw new Error(
'MongoDB instance already in status startup/running/error. Use opts.debug = true for more info.'
);
}

this.runningInstance = this._startUpInstance()
.catch(err => {
if (err.message === 'Mongod shutting down' || err === 'Mongod shutting down') {
this.debug(`Mongodb does not started. Trying to start on another port one more time...`);
this.opts.instance.port = null;
return this._startUpInstance();
}
throw err;
})
.catch(err => {
if (!this.opts.debug) {
throw new Error(
`${err.message}\n\nUse debug option for more info: new MongoMemoryServer({ debug: true })`
);
}
throw err;
});

return this.runningInstance.then(() => true);
}

async _startUpInstance(): Promise<MongoInstanceDataT> {
const data = {};
let tmpDir;

const instOpts = this.opts.instance;
data.port = await getport(instOpts.port);
data.uri = await generateConnectionString(data.port);
data.storageEngine = instOpts.storageEngine || 'ephemeralForTest';
if (instOpts.dbPath) {
data.dbPath = instOpts.dbPath;
} else {
tmpDir = tmp.dirSync({ prefix: 'mongo-mem-', unsafeCleanup: true });
data.dbPath = tmpDir.name;
}

this.debug(`Starting MongoDB instance with following options: ${JSON.stringify(data)}`);

// Download if not exists mongo binaries in ~/.mongodb-prebuilt
// After that startup MongoDB instance
const mongod = await MongoInstance.run({
instance: {
port: data.port,
storageEngine: data.storageEngine,
dbPath: data.dbPath,
debug: this.opts.instance.debug,
},
binary: this.opts.binary,
spawn: this.opts.spawn,
debug: this.opts.debug,
});
data.mongod = mongod;
data.tmpDir = tmpDir;

return data;
}

async stop(): Promise<boolean> {
const { mongod, port, tmpDir } = (await this.getInstanceData(): MongoInstanceDataT);

if (mongod && mongod.kill) {
this.debug(`Shutdown MongoDB server on port ${port} with pid ${mongod.pid}`);
mongod.kill();
}

if (tmpDir) {
this.debug(`Removing tmpDir ${tmpDir.name}`);
tmpDir.removeCallback();
}

this.runningInstance = null;
return true;
}

async getInstanceData(): Promise<MongoInstanceDataT> {
if (this.runningInstance) {
return this.runningInstance;
}
throw new Error(
'Database instance is not running. You should start database by calling start() method. BTW it should start automatically if opts.autoStart!=false. Also you may provide opts.debug=true for more info.'
);
}

async getUri(otherDbName?: string | boolean = false): Promise<string> {
const { uri, port } = (await this.getInstanceData(): MongoInstanceDataT);

// IF true OR string
if (otherDbName) {
if (typeof otherDbName === 'string') {
// generate uri with provided DB name on existed DB instance
return generateConnectionString(port, otherDbName);
}
// generate new random db name
return generateConnectionString(port);
}

return uri;
}

async getConnectionString(otherDbName: string | boolean = false): Promise<string> {
return this.getUri(otherDbName);
}

async getPort(): Promise<number> {
const { port } = (await this.getInstanceData(): MongoInstanceDataT);
return port;
}

async getDbPath(): Promise<string> {
const { dbPath } = (await this.getInstanceData(): MongoInstanceDataT);
return dbPath;
}
}
2 changes: 1 addition & 1 deletion src/__tests__/multipleDB-test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* @flow */

import MongoDBMemoryServer from '../index';
import { MongoClient } from 'mongodb';
import MongoDBMemoryServer from '../MongoMemoryServer';

jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;

Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/singleDB-test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* @flow */

import MongoDBMemoryServer from '../index';
import { MongoClient } from 'mongodb';
import MongoDBMemoryServer from '../MongoMemoryServer';

jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;

Expand Down

0 comments on commit 0bd048a

Please sign in to comment.