diff --git a/.eslintrc.json b/.eslintrc.json index 1294b94..d02bb36 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,4 +1,7 @@ { + "parserOptions": { + "ecmaVersion": 2018 + }, "env": { "es6": true, "browser": false @@ -15,4 +18,4 @@ "no-new": "warn", "prefer-arrow-callback": "warn" } -} \ No newline at end of file +} diff --git a/README.md b/README.md index 5debb99..2e78675 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,9 @@ const settings = { const connector = new PostgresConnector( settings ) +// start connector +connector.init() + connector.on( 'ready', ()=>{ connector.subscribe( event =>{ //event will be a map of event and table for CREATE_TABLE and DESTROY_TABLE diff --git a/package-lock.json b/package-lock.json index 4023bea..7e5d6da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@deepstream/storage-postgres", - "version": "2.0.1", + "version": "3.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index df89b65..420ec3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@deepstream/storage-postgres", - "version": "2.0.1", + "version": "3.0.0", "description": "A deepstream.io storage connector for use with deepstream", "main": "src/connector.js", "scripts": { diff --git a/src/connector.js b/src/connector.js index 9a6249e..a38d2fb 100644 --- a/src/connector.js +++ b/src/connector.js @@ -1,23 +1,23 @@ -"use strict"; +'use strict' -const UNDEFINED_TABLE = "42P01"; -const INTERNAL_ERROR = "XX000"; -const DATABASE_IS_STARTING_UP = "57P03"; -const CONNECTION_REFUSED = "ECONNREFUSED"; -const events = require( "events" ); -const util = require( "util" ); -const pckg = require( "../package.json" ); +const UNDEFINED_TABLE = '42P01' +const INTERNAL_ERROR = 'XX000' +const DATABASE_IS_STARTING_UP = '57P03' +const CONNECTION_REFUSED = 'ECONNREFUSED' +const events = require('events') +const pckg = require('../package.json') // native has a getter specified that will lazily load the module // if the native module is not available it will log "Cannot find module 'pg-native'" // could surpress this by temporarily disabling the console... // unfortunately, multiple queries per statement fail using the native module // disabling until resolved -const pg = /*require('pg').native ||*/ require("pg"); -const Statements = require( "./statements" ); -const utils = require( "./utils" ); -const SchemaListener = require( "./schema-listener" ); -const WriteOperation = require( "./write-operation" ); +const pg = /* require('pg').native || */ require('pg') +const Statements = require('./statements') +const utils = require('./utils') +const SchemaListener = require('./schema-listener') +const WriteOperation = require('./write-operation') +const transformData = require('./transform-data') /** * Class deepstream.io postgres database connector @@ -56,29 +56,28 @@ module.exports = class Connector extends events.EventEmitter { * @constructor * @returns {void} */ - constructor( options, services ) { - super(); - this.isReady = false; - this.name = pckg.name; - this.version = pckg.version; - this._structure = {}; - this.options = options; - this.services = services; - - this.description = `postgres connection to ${this.options.host} and database ${this.options.database} ${pckg.version}`; + constructor (options, services) { + super() + this.isReady = false + this.name = pckg.name + this.version = pckg.version + this._structure = {} + this.options = options + this.services = services + this.description = `postgres connection to ${this.options.host} and database ${this.options.database} ${pckg.version}` } init () { - this.on('error', error => this.services.logger.fatal(error)); + this.on('error', error => this.services.logger.fatal(error)) - this._checkOptions(); - this.statements = new Statements( this.options ); - this._connectionPool = new pg.Pool( this.options ); - this._connectionPool.on( "error", this._checkError.bind( this ) ); - this._schemaListener = new SchemaListener( this._connectionPool ); - this._writeOperations = {}; - this._initialise(); - this._flushInterval = setInterval( this._flushWrites.bind( this ), this.options.writeInterval ); + this._checkOptions() + this.statements = new Statements(this.options) + this._connectionPool = new pg.Pool(this.options) + this._connectionPool.on('error', this._checkError.bind(this)) + this._schemaListener = new SchemaListener(this._connectionPool) + this._writeOperations = {} + this._initialise() + this._flushInterval = setInterval(this._flushWrites.bind(this), this.options.writeInterval) } async whenReady () { @@ -100,10 +99,10 @@ module.exports = class Connector extends events.EventEmitter { * @public * @returns {void} */ - destroy( callback ) { - clearInterval( this._flushInterval ); - this._schemaListener.destroy(); - this._connectionPool.end( callback ); + destroy (callback) { + clearInterval(this._flushInterval) + this._schemaListener.destroy() + this._connectionPool.end(callback) } /** @@ -115,9 +114,9 @@ module.exports = class Connector extends events.EventEmitter { * @public * @returns {void} */ - createSchema( name, callback ) { - var statement = this.statements.createSchema({ name: name, owner: this.options.user }); - this.query( statement, callback, null, true ); + createSchema (name, callback) { + var statement = this.statements.createSchema({ name: name, owner: this.options.user }) + this.query(statement, callback, null, true) } /** @@ -129,9 +128,9 @@ module.exports = class Connector extends events.EventEmitter { * @public * @returns {void} */ - destroySchema( name, callback ) { - var statement = this.statements.destroySchema({ name: name }); - this.query( statement, callback, null, true ); + destroySchema (name, callback) { + var statement = this.statements.destroySchema({ name: name }) + this.query(statement, callback, null, true) } /** @@ -144,20 +143,20 @@ module.exports = class Connector extends events.EventEmitter { * @public * @returns {void} */ - getSchemaOverview( callback, name ) { - name = name || this.options.schema; - var statement = this.statements.getOverview({ schema: name }); - this.query( statement, (error, result ) => { - if ( error ) { - callback( error ); + getSchemaOverview (callback, name) { + name = name || this.options.schema + var statement = this.statements.getOverview({ schema: name }) + this.query(statement, (error, result) => { + if (error) { + callback(error) } else { - var tables = {}, i; - for ( i = 0; i < result.rows.length; i++ ) { - tables[ result.rows[ i ].table ] = result.rows[ i ].entries; + var tables = {}, i + for (i = 0; i < result.rows.length; i++) { + tables[ result.rows[ i ].table ] = result.rows[ i ].entries } - callback( null, tables ); + callback(null, tables) } - }, null, true ); + }, null, true) } /** @@ -173,9 +172,9 @@ module.exports = class Connector extends events.EventEmitter { * @public * @returns {void} */ - subscribe( callback, done, schema ) { - schema = schema || this.options.schema; - this._schemaListener.getNotificationsForSchema( schema, callback, done ); + subscribe (callback, done, schema) { + schema = schema || this.options.schema + this._schemaListener.getNotificationsForSchema(schema, callback, done) } /** @@ -189,9 +188,9 @@ module.exports = class Connector extends events.EventEmitter { * @public * @returns {void} */ - unsubscribe( callback, done, schema ) { - schema = schema || this.options.schema; - this._schemaListener.unsubscribeFromNotificationsForSchema( schema, callback, done ); + unsubscribe (callback, done, schema) { + schema = schema || this.options.schema + this._schemaListener.unsubscribeFromNotificationsForSchema(schema, callback, done) } /** @@ -205,15 +204,17 @@ module.exports = class Connector extends events.EventEmitter { * @public * @returns {void} */ - set( key, version, value, callback ) { - const params = utils.parseKey( key, this.options ); - const tableName = params.schema + params.table; + set (key, version, value, callback) { + const params = utils.parseKey(key, this.options) + const tableName = params.schema + params.table - if ( !this._writeOperations[ tableName ] ) { - this._writeOperations[ tableName ] = new WriteOperation( params, this ); + if (!this._writeOperations[ tableName ]) { + this._writeOperations[ tableName ] = new WriteOperation(params, this) } - this._writeOperations[ tableName ].add( params.key, { _d: value, _v: version }, callback ); + const data = transformData.transformValueForStorage(version, value) + + this._writeOperations[ tableName ].add(params.key, data, callback) } /** @@ -226,24 +227,20 @@ module.exports = class Connector extends events.EventEmitter { * @public * @returns {void} */ - get( key, callback ) { - this.query( this.statements.get( utils.parseKey( key, this.options ) ), ( error, result ) => { - if ( error && error.code === UNDEFINED_TABLE ) { - callback( null, -1, null ); - } else if ( error ) { - callback( error ); - } else if ( result.rows.length === 0 ) { - callback( null, -1, null ); + get (key, callback) { + this.query(this.statements.get(utils.parseKey(key, this.options)), (error, result) => { + if (error && error.code === UNDEFINED_TABLE) { + callback(null, -1, null) + } else if (error) { + callback(error) + } else if (result.rows.length === 0) { + callback(null, -1, null) } else { - if ( typeof result.rows[ 0 ].val !== "string" ) { - callback( null, result.rows[ 0 ].val._v, result.rows[ 0 ].val._d ); - } else { - const r = JSON.parse( result.rows[ 0 ].val ) - callback( null, r._v, r._d ); - } + const value = transformData.transformValueFromStorage( result.rows[ 0 ].val ) + callback(null, value._v, value._d) } - }, null, true ); + }, null, true) } /** @@ -257,9 +254,9 @@ module.exports = class Connector extends events.EventEmitter { * @public * @returns {void} */ - delete( key, callback ) { - var statement = this.statements.delete( utils.parseKey( key, this.options ) ); - this.query( statement, callback ); + delete (key, callback) { + var statement = this.statements.delete(utils.parseKey(key, this.options)) + this.query(statement, callback) } /** @@ -273,18 +270,18 @@ module.exports = class Connector extends events.EventEmitter { * @public * @returns {void} */ - query( query, callback, args, silent ) { - this._connectionPool.connect( ( error, client, done ) => { - this._checkError( error, "failed to get connection from the pool" ); - if ( error ) { return callback( error ); } - client.query( query, args || [], ( error, result ) => { - done(); - if ( !silent ) { - this._checkError( error, "error during query " + query ); + query (query, callback, args, silent) { + this._connectionPool.connect((error, client, done) => { + this._checkError(error, 'failed to get connection from the pool') + if (error) { return callback(error) } + client.query(query, args || [], (error, result) => { + done() + if (!silent) { + this._checkError(error, 'error during query ' + query) } - callback( error, result ); - }); - }); + callback(error, result) + }) + }) } /** @@ -295,12 +292,12 @@ module.exports = class Connector extends events.EventEmitter { * @private * @returns {void} */ - _flushWrites() { - for ( var tableName in this._writeOperations ) { - if ( this._writeOperations[ tableName ].isEmpty ) { - delete this._writeOperations[ tableName ]; + _flushWrites () { + for (var tableName in this._writeOperations) { + if (this._writeOperations[ tableName ].isEmpty) { + delete this._writeOperations[ tableName ] } else { - this._writeOperations[ tableName ].execute(); + this._writeOperations[ tableName ].execute() } } } @@ -313,23 +310,23 @@ module.exports = class Connector extends events.EventEmitter { * @private * @returns {void} */ - _checkOptions( options ) { - this._checkOption( "user", "string" ); - //this._checkOption( 'password', 'string' ) - this._checkOption( "host", "string" ); - this._checkOption( "port", "number", 5432 ); - this._checkOption( "max", "number", 10 ); - this._checkOption( "idleTimeoutMillis", "number", 30000 ); - this._checkOption( "writeInterval", "number", 200 ); - this._checkOption( "schema", "string", "ds" ); - this._checkOption( "useJsonb", "boolean", false ); - this._checkOption( "notifications", "object", { + _checkOptions () { + this._checkOption('user', 'string') + // this._checkOption( 'password', 'string' ) + this._checkOption('host', 'string') + this._checkOption('port', 'number', 5432) + this._checkOption('max', 'number', 10) + this._checkOption('idleTimeoutMillis', 'number', 30000) + this._checkOption('writeInterval', 'number', 200) + this._checkOption('schema', 'string', 'ds') + this._checkOption('useJsonb', 'boolean', false) + this._checkOption('notifications', 'object', { CREATE_TABLE: false, DESTROY_TABLE: false, INSERT: false, UPDATE: false, - DELETE: false, - }); + DELETE: false + }) } /** @@ -344,13 +341,13 @@ module.exports = class Connector extends events.EventEmitter { * @private * @returns {void} */ - _checkOption( name, type, defaultValue ) { - if ( this.options[ name ] === undefined && defaultValue !== undefined ) { - this.options[ name ] = defaultValue; + _checkOption (name, type, defaultValue) { + if (this.options[ name ] === undefined && defaultValue !== undefined) { + this.options[ name ] = defaultValue } - if ( typeof this.options[ name ] !== type ) { - throw new Error( "missing option " + name ); + if (typeof this.options[ name ] !== type) { + throw new Error('missing option ' + name) } } @@ -366,23 +363,23 @@ module.exports = class Connector extends events.EventEmitter { * @private * @returns {void} */ - _initialise() { - this.query( this.statements.initDb( this.options.schema ), ( error, result ) => { - if ( error ) { + _initialise () { + this.query(this.statements.initDb(this.options.schema), (error, result) => { + if (error) { // retry for errors caused by concurrent initialisation // or when the DB can't be reached (e.g. it's still starting up in a Docker setup) - if ( error.code === INTERNAL_ERROR || + if (error.code === INTERNAL_ERROR || error.code === DATABASE_IS_STARTING_UP || - error.code === CONNECTION_REFUSED ) { - return this._initialise(); + error.code === CONNECTION_REFUSED) { + return this._initialise() } else { - throw error; + throw error } } - utils.checkVersion(result[result.length - 1].rows[0].version); - this.isReady = true; - this.emit( "ready" ); - }, null, true ); + utils.checkVersion(result[result.length - 1].rows[0].version) + this.isReady = true + this.emit('ready') + }, null, true) } /** @@ -395,9 +392,9 @@ module.exports = class Connector extends events.EventEmitter { * @private * @returns {void} */ - _checkError( error, message ) { - if ( error && error.code !== DATABASE_IS_STARTING_UP && error.code !== CONNECTION_REFUSED ) { - console.log( error, message ); + _checkError (error, message) { + if (error && error.code !== DATABASE_IS_STARTING_UP && error.code !== CONNECTION_REFUSED) { + console.log(error, message) } } -}; +} diff --git a/src/transform-data.js b/src/transform-data.js new file mode 100644 index 0000000..cb3566b --- /dev/null +++ b/src/transform-data.js @@ -0,0 +1,57 @@ +'use strict' + +/** + * This method is for the storage connector, to allow queries to happen more naturally + * do not use in cache connectors + * + * Inverts the data from the deepstream structure to reduce nesting. + * + * { _v: 1, _d: { name: 'elasticsearch' } } -> { name: 'elasticsearch', __ds = { _v: 1 } } + * { _v: 1, _d: ['list'] } -> { __dsList: ['list'], __ds = { _v: 1 } } + * + * @param {Number} version The data version + * @param {Object} value The data to save + * @private + * @returns {Object} data + */ +module.exports.transformValueForStorage = function (version, value) { + let data + if (value instanceof Array) { + data = { __dsList: value, __ds: { _v: version } } + } else { + data = { ...value, __ds: { _v: version } } + } + + return data +} + +/** + * This method is for the storage connector, to allow queries to happen more naturally + * do not use in cache connectors + * + * Inverts the data from the stored structure back to the deepstream structure + * + * { name: 'elasticsearch', __ds = { _v: 1 } } -> { _v: 1, _d: { name: 'elasticsearch' } } + * { __dsList: ['list'], __ds = { _v: 1 } } -> { _v: 1, _d: ['list'] } + * + * @param {String|Object} value The data to transform + * + * @private + * @returns {Object} data + */ +module.exports.transformValueFromStorage = function (value) { + if (typeof value === 'string') { + value = JSON.parse(value) + } + + let data = value.__ds + delete value.__ds + + if (value.__dsList instanceof Array) { + data._d = value.__dsList + } else { + data._d = value + } + + return data +} diff --git a/test/cache-connector.spec.js b/test/cache-connector.spec.js index 7b93660..a3f5eda 100644 --- a/test/cache-connector.spec.js +++ b/test/cache-connector.spec.js @@ -1,16 +1,18 @@ -"use strict"; +'use strict' /* global describe, expect, it, jasmine */ -const expect = require("chai").expect; -const DbConnector = require("../src/connector"); -const EventEmitter = require("events").EventEmitter; +const expect = require('chai').expect +const DbConnector = require('../src/connector') +const EventEmitter = require('events').EventEmitter const settings = { user: process.env.PGUSER || 'postgres', database: process.env.PGDATABASE || 'postgres', password: process.env.PGPASSWORD || 'mysecretpassword', host: process.env.PGHOST || 'localhost', - port: parseInt( process.env.PGPORT, 10 ) || 5432, + port: parseInt(process.env.PGPORT, 10) || 5432, + schema: 'dstest', + useJsonb: true, // store values as searchable binary JSON (slower) max: 10, idleTimeoutMillis: 30000, notifications: { @@ -18,488 +20,488 @@ const settings = { DESTROY_TABLE: true, INSERT: true, UPDATE: false, - DELETE: true, - }, -}; + DELETE: true + } +} -describe("connector", () => { - describe( "the message connector has the correct structure", () => { - var dbConnector; +describe('connector', () => { + describe('the message connector has the correct structure', () => { + var dbConnector - it( "creates the dbConnector", async () => { - dbConnector = new DbConnector( settings ); + it('creates the dbConnector', async () => { + dbConnector = new DbConnector(settings) dbConnector.init() - await dbConnector.whenReady(); - }); + await dbConnector.whenReady() + }) - it( "implements the cache/storage connector interface", () => { - expect( dbConnector.get ).to.be.a( "function" ); - expect( dbConnector.set ).to.be.a( "function" ); - expect( dbConnector.delete ).to.be.a( "function" ); - expect( dbConnector instanceof EventEmitter ).to.equal( true ); - }); + it('implements the cache/storage connector interface', () => { + expect(dbConnector.get).to.be.a('function') + expect(dbConnector.set).to.be.a('function') + expect(dbConnector.delete).to.be.a('function') + expect(dbConnector instanceof EventEmitter).to.equal(true) + }) - it( "destroys the connector", done => { - dbConnector.destroy( done ); - }); - }); + it('destroys the connector', done => { + dbConnector.destroy(done) + }) + }) - describe( "creates multiple connectors in parallel", () => { - var connectors = []; + describe('creates multiple connectors in parallel', () => { + var connectors = [] - it( "creates four connectors", ( done ) => { - var ready = 0, i, num = 4, conn; + it('creates four connectors', (done) => { + var ready = 0, i, num = 4, conn - for ( i = 0; i < num; i++ ) { - conn = new DbConnector( settings ); + for (i = 0; i < num; i++) { + conn = new DbConnector(settings) conn.init() - conn.on( "ready", () => { - ready++; - if ( ready === num ) { - done(); + conn.on('ready', () => { + ready++ + if (ready === num) { + done() } - }); - connectors.push( conn ); + }) + connectors.push(conn) } - }); + }) - it( "destroys four connectors", ( done ) => { - var ready = connectors.length, i; + it('destroys four connectors', (done) => { + var ready = connectors.length, i - for ( i = 0; i < connectors.length; i++ ) { + for (i = 0; i < connectors.length; i++) { connectors[ i ].destroy(() => { - ready--; - if ( ready === 0 ) { - done(); + ready-- + if (ready === 0) { + done() } - }); + }) } - }); - }); + }) + }) - describe( "creates databases", () => { - var dbConnector; + describe('creates databases', () => { + var dbConnector - it( "creates the dbConnector", ( done ) => { - dbConnector = new DbConnector( settings ); - expect( dbConnector.isReady ).to.equal( false ); + it('creates the dbConnector', (done) => { + dbConnector = new DbConnector(settings) + expect(dbConnector.isReady).to.equal(false) dbConnector.init() - dbConnector.on( "ready", done ); - }); - - it( "creates a schema", ( done ) => { - dbConnector.createSchema( "some-schema", ( err, result ) => { - expect( err ).to.equal( null ); - expect( result.command ).to.equal( "CREATE" ); - done(); - }); - }); - - it( "deletes a schema", ( done ) => { - dbConnector.destroySchema( "some-schema", ( err, result ) => { - expect( err ).to.equal( null ); - expect( result.command ).to.equal( "DROP" ); - done(); - }); - }); - - it( "destroys the connector", done => { - dbConnector.destroy( done ); - }); - }); - - describe( "sets and gets values", () => { - var dbConnector, lastMessage = null, messages = []; - const ITEM_NAME = "some-table/some-key"; - - it( "creates the dbConnector", ( done ) => { - dbConnector = new DbConnector( settings ); + dbConnector.on('ready', done) + }) + + it('creates a schema', (done) => { + dbConnector.createSchema('some-schema', (err, result) => { + expect(err).to.equal(null) + expect(result.command).to.equal('CREATE') + done() + }) + }) + + it('deletes a schema', (done) => { + dbConnector.destroySchema('some-schema', (err, result) => { + expect(err).to.equal(null) + expect(result.command).to.equal('DROP') + done() + }) + }) + + it('destroys the connector', done => { + dbConnector.destroy(done) + }) + }) + + describe('sets and gets values', () => { + var dbConnector, lastMessage = null, messages = [] + const ITEM_NAME = 'some-table/some-key' + + it('creates the dbConnector', (done) => { + dbConnector = new DbConnector(settings) dbConnector.init() - expect( dbConnector.isReady ).to.equal( false ); - dbConnector.on( "ready", done ); - }); - - it( "subscribes to notifications", ( done ) => { - dbConnector.subscribe( ( msg ) => { - messages.push( msg ); - lastMessage = msg; - }, done ); - }); - - it( "retrieves a non existing value", ( done ) => { - dbConnector.get( ITEM_NAME, ( error, version, value ) => { - expect( error ).to.equal( null ); - expect( version ).to.equal( -1 ); - expect( value ).to.equal( null ); - done(); - }); - }); - - it( "sets a value for a non existing table", ( done ) => { - expect( lastMessage ).to.be.null; - dbConnector.set( ITEM_NAME, 10,{ firstname: "Wolfram" }, ( error ) => { - expect( error ).to.equal( null ); - //give it some time to receive the notifications - setTimeout( done, 300 ); - }); - }); - - it( "received a notification", () => { - expect( messages.length ).to.equal( 2 ); - expect( messages[ 0 ] ).to.deep.equal({ event: "CREATE_TABLE", table: "some-table" }); - expect( messages[ 1 ] ).to.deep.equal({ event: "INSERT", table: "some-table", key: "some-key" }); - }); - - it( "retrieves an existing value", ( done ) => { - dbConnector.get( ITEM_NAME, ( error, version, value ) => { - expect( error ).to.equal( null ); - expect( version ).to.equal(10); - expect( value ).to.deep.equal( { firstname: "Wolfram" } ); - done(); - }); - }); - - it( "sets a value for an existing table", ( done ) => { - expect( messages.length ).to.equal( 2 ); - dbConnector.set( "some-table/another-key", 10 , { firstname: "Egon" }, ( error ) => { - expect( error ).to.equal( null ); - //give it some time to receive the notifications - setTimeout( done, 300 ); - }); - }); - - it( "received a notification", () => { - expect( messages.length ).to.equal( 3 ); - expect( messages[ 2 ] ).to.deep.equal({ event: "INSERT", table: "some-table", key: "another-key" }); - }); - - it( "deletes a value", ( done ) => { - dbConnector.delete( ITEM_NAME, ( error ) => { - expect( error ).to.equal( null ); - //give it some time to receive the notifications - setTimeout( done, 300 ); - }); - }); - - it( "received a notification", () => { - expect( messages.length ).to.equal( 4 ); - expect( messages[ 3 ] ).to.deep.equal({ event: "DELETE", table: "some-table", key: "some-key" }); - }); - - it( "Can't retrieve a deleted value", ( done ) => { - dbConnector.get( ITEM_NAME, ( error, version, value ) => { - expect( error ).to.equal( null ); - expect( version ).to.equal( -1 ); - expect( value ).to.equal( null ); - done(); - }); - }); - - it( "destroys the connector", done => { - dbConnector.destroy( done ); - }); - }); - - describe( "advanced sets", () => { - var dbConnector, lastMessage = null, messages = []; - const ITEM_NAME = "some-other-table/some-other-key"; - - it( "creates the dbConnector", ( done ) => { - dbConnector = new DbConnector( settings ); - dbConnector.init(); - expect( dbConnector.isReady ).to.equal( false ); - dbConnector.on( "ready", done ); - }); - - it( "subscribes to notifications", ( done ) => { - dbConnector.subscribe( ( msg ) => { - messages.push( msg ); - }, done); - }); - - it( "sets a value for a non existing table", ( done ) => { - dbConnector.set( ITEM_NAME, 10, { testValue: "A" }, ( error ) => { - expect( error ).to.equal( null ); - done(); - }); - }); - - it( "sets value B", ( done ) => { - dbConnector.set( ITEM_NAME, 10, { testValue: "B" }, ( error ) => { - expect( error ).to.equal( null ); - done(); - }); - }); - - it( "sets value C", ( done ) => { - dbConnector.set( ITEM_NAME, 10, { testValue: "C" }, ( error ) => { - expect( error ).to.equal( null ); - done(); - }); - }); - - it( "sets value D", ( done ) => { - dbConnector.set( ITEM_NAME, 10, { testValue: "D" }, ( error ) => { - expect( error ).to.equal( null ); - done(); - }); - }); - - it( "gets the latest value", ( done ) => { - dbConnector.get( ITEM_NAME, ( error, version, item ) => { - expect( error ).to.be.null; - expect( item.testValue ).to.equal( "D" ); - done(); - }); - }); - - it( "received the right notifications", () => { - expect( messages.length ).to.equal( 2 ); - expect( messages[ 0 ].event ).to.equal( "CREATE_TABLE" ); - expect( messages[ 1 ].event ).to.equal( "INSERT" ); - messages = []; - }); - - it( "writes multiple values in quick succession to an existing table", ( done ) => { - dbConnector.set( "some-table/itemA", 1, { val: 1 }, () => {}); - dbConnector.set( "some-table/itemA", 1, { val: 2 }, () => {}); - dbConnector.set( "some-table/itemA", 1, { val: 3 }, () => {}); - dbConnector.set( "some-table/itemA", 1, { val: 4 }, () => {}); - dbConnector.set( "some-table/itemA", 1, { val: 5 }, ( error ) => { - expect( error ).to.be.null; - done(); - }); - }); - - it( "retrieves the latest item from the last operation", ( done ) => { - dbConnector.get( "some-table/itemA", ( error, version, item ) => { - expect( error ).to.be.null; - expect( item.val ).to.equal( 5 ); - done(); - }); - }); - - it( "received the right notifications", () => { - expect( messages.length ).to.equal( 1 ); - expect( messages[ 0 ].event ).to.equal( "INSERT" ); - expect( messages[ 0 ].table ).to.equal( "some-table" ); - expect( messages[ 0 ].key ).to.equal( "itemA" ); - messages = []; - }); - - it( "writes multiple values in quick succession to a new table", ( done ) => { - dbConnector.set( "new-table/itemA", 1, { val: 6 }, () => {}); - dbConnector.set( "new-table/itemA", 1, { val: 7 }, () => {}); - dbConnector.set( "new-table/itemA", 1, { val: 8 }, () => {}); - dbConnector.set( "new-table/itemA", 1, { val: 9 }, () => {}); - dbConnector.set( "new-table/itemA", 1, { val: 10 }, ( error ) => { - expect( error ).to.be.null; - done(); - }); - }); - - it( "retrieves the latest item from the last operation", ( done ) => { - dbConnector.get( "new-table/itemA", ( error, version, item ) => { - expect( error ).to.be.null; - expect( item.val ).to.equal( 10 ); - done(); - }); - }); - - it( "received the right notifications", () => { - expect( messages.length ).to.equal( 2 ); - expect( messages[ 0 ].event ).to.equal( "CREATE_TABLE" ); - expect( messages[ 1 ].event ).to.equal( "INSERT" ); - messages = []; - }); - - it( "writes a combination of values in quick succession", ( done ) => { - let doneListener = new EventEmitter(); - - let setgets = [ "aa", "ab", "ba", "bb" ]; - - doneListener.on("set-get-done", val => { - setgets.splice(setgets.indexOf(val), 1); + expect(dbConnector.isReady).to.equal(false) + dbConnector.on('ready', done) + }) + + it('subscribes to notifications', (done) => { + dbConnector.subscribe((msg) => { + messages.push(msg) + lastMessage = msg + }, done) + }) + + it('retrieves a non existing value', (done) => { + dbConnector.get(ITEM_NAME, (error, version, value) => { + expect(error).to.equal(null) + expect(version).to.equal(-1) + expect(value).to.equal(null) + done() + }) + }) + + it('sets a value for a non existing table', (done) => { + expect(lastMessage).to.be.null + dbConnector.set(ITEM_NAME, 10, { firstname: 'Wolfram' }, (error) => { + expect(error).to.equal(null) + // give it some time to receive the notifications + setTimeout(done, 300) + }) + }) + + it('received a notification', () => { + expect(messages.length).to.equal(2) + expect(messages[ 0 ]).to.deep.equal({ event: 'CREATE_TABLE', table: 'some-table' }) + expect(messages[ 1 ]).to.deep.equal({ event: 'INSERT', table: 'some-table', key: 'some-key' }) + }) + + it('retrieves an existing value', (done) => { + dbConnector.get(ITEM_NAME, (error, version, value) => { + expect(error).to.equal(null) + expect(version).to.equal(10) + expect(value).to.deep.equal({ firstname: 'Wolfram' }) + done() + }) + }) + + it('sets a value for an existing table', (done) => { + expect(messages.length).to.equal(2) + dbConnector.set('some-table/another-key', 10, { firstname: 'Egon' }, (error) => { + expect(error).to.equal(null) + // give it some time to receive the notifications + setTimeout(done, 300) + }) + }) + + it('received a notification', () => { + expect(messages.length).to.equal(3) + expect(messages[ 2 ]).to.deep.equal({ event: 'INSERT', table: 'some-table', key: 'another-key' }) + }) + + it('deletes a value', (done) => { + dbConnector.delete(ITEM_NAME, (error) => { + expect(error).to.equal(null) + // give it some time to receive the notifications + setTimeout(done, 300) + }) + }) + + it('received a notification', () => { + expect(messages.length).to.equal(4) + expect(messages[ 3 ]).to.deep.equal({ event: 'DELETE', table: 'some-table', key: 'some-key' }) + }) + + it("Can't retrieve a deleted value", (done) => { + dbConnector.get(ITEM_NAME, (error, version, value) => { + expect(error).to.equal(null) + expect(version).to.equal(-1) + expect(value).to.equal(null) + done() + }) + }) + + it('destroys the connector', done => { + dbConnector.destroy(done) + }) + }) + + describe('advanced sets', () => { + var dbConnector, lastMessage = null, messages = [] + const ITEM_NAME = 'some-other-table/some-other-key' + + it('creates the dbConnector', (done) => { + dbConnector = new DbConnector(settings) + dbConnector.init() + expect(dbConnector.isReady).to.equal(false) + dbConnector.on('ready', done) + }) + + it('subscribes to notifications', (done) => { + dbConnector.subscribe((msg) => { + messages.push(msg) + }, done) + }) + + it('sets a value for a non existing table', (done) => { + dbConnector.set(ITEM_NAME, 10, { testValue: 'A' }, (error) => { + expect(error).to.equal(null) + done() + }) + }) + + it('sets value B', (done) => { + dbConnector.set(ITEM_NAME, 10, { testValue: 'B' }, (error) => { + expect(error).to.equal(null) + done() + }) + }) + + it('sets value C', (done) => { + dbConnector.set(ITEM_NAME, 10, { testValue: 'C' }, (error) => { + expect(error).to.equal(null) + done() + }) + }) + + it('sets value D', (done) => { + dbConnector.set(ITEM_NAME, 10, { testValue: 'D' }, (error) => { + expect(error).to.equal(null) + done() + }) + }) + + it('gets the latest value', (done) => { + dbConnector.get(ITEM_NAME, (error, version, item) => { + expect(error).to.be.null + expect(item.testValue).to.equal('D') + done() + }) + }) + + it('received the right notifications', () => { + expect(messages.length).to.equal(2) + expect(messages[ 0 ].event).to.equal('CREATE_TABLE') + expect(messages[ 1 ].event).to.equal('INSERT') + messages = [] + }) + + it('writes multiple values in quick succession to an existing table', (done) => { + dbConnector.set('some-table/itemA', 1, { val: 1 }, () => {}) + dbConnector.set('some-table/itemA', 1, { val: 2 }, () => {}) + dbConnector.set('some-table/itemA', 1, { val: 3 }, () => {}) + dbConnector.set('some-table/itemA', 1, { val: 4 }, () => {}) + dbConnector.set('some-table/itemA', 1, { val: 5 }, (error) => { + expect(error).to.be.null + done() + }) + }) + + it('retrieves the latest item from the last operation', (done) => { + dbConnector.get('some-table/itemA', (error, version, item) => { + expect(error).to.be.null + expect(item.val).to.equal(5) + done() + }) + }) + + it('received the right notifications', () => { + expect(messages.length).to.equal(1) + expect(messages[ 0 ].event).to.equal('INSERT') + expect(messages[ 0 ].table).to.equal('some-table') + expect(messages[ 0 ].key).to.equal('itemA') + messages = [] + }) + + it('writes multiple values in quick succession to a new table', (done) => { + dbConnector.set('new-table/itemA', 1, { val: 6 }, () => {}) + dbConnector.set('new-table/itemA', 1, { val: 7 }, () => {}) + dbConnector.set('new-table/itemA', 1, { val: 8 }, () => {}) + dbConnector.set('new-table/itemA', 1, { val: 9 }, () => {}) + dbConnector.set('new-table/itemA', 1, { val: 10 }, (error) => { + expect(error).to.be.null + done() + }) + }) + + it('retrieves the latest item from the last operation', (done) => { + dbConnector.get('new-table/itemA', (error, version, item) => { + expect(error).to.be.null + expect(item.val).to.equal(10) + done() + }) + }) + + it('received the right notifications', () => { + expect(messages.length).to.equal(2) + expect(messages[ 0 ].event).to.equal('CREATE_TABLE') + expect(messages[ 1 ].event).to.equal('INSERT') + messages = [] + }) + + it('writes a combination of values in quick succession', (done) => { + let doneListener = new EventEmitter() + + let setgets = [ 'aa', 'ab', 'ba', 'bb' ] + + doneListener.on('set-get-done', val => { + setgets.splice(setgets.indexOf(val), 1) if (!setgets.length) { - done(); + done() } - }); - - dbConnector.set( "table-a/item-a", 1, { val: "aa" }, (error) => { - expect( error ).to.be.null; - dbConnector.get( "table-a/item-a", ( error, version, item ) => { - expect( error ).to.be.null; - expect( item.val ).to.equal( "aa" ); - doneListener.emit("set-get-done", "aa"); - }); - }); - - dbConnector.set( "table-a/item-b", 1, { val: "ab" }, (error) => { - expect( error ).to.be.null; - dbConnector.get( "table-a/item-b", ( error, version, item ) => { - expect( error ).to.be.null; - expect( item.val ).to.equal( "ab" ); - doneListener.emit("set-get-done", "ab"); - }); - }); - - dbConnector.set( "table-b/item-a", 1, { val: "ba" }, (error) => { - expect( error ).to.be.null; - dbConnector.get( "table-b/item-a", ( error, version, item ) => { - expect( error ).to.be.null; - expect( item.val ).to.equal( "ba" ); - doneListener.emit("set-get-done", "ba"); - }); - }); - - dbConnector.set( "table-b/item-b", 1, { val: "bb" }, ( error ) => { - expect( error ).to.be.null; - dbConnector.get( "table-b/item-b", ( error, version, item ) => { - expect( error ).to.be.null; - expect( item.val ).to.equal( "bb" ); - doneListener.emit("set-get-done", "bb"); - }); - }); - }); - - it( "received the right notifications", () => { - expect( messages.length ).to.equal( 6 ); - var create = 0; - var update = 0; - for ( var i = 0; i < messages.length; i++ ) { - if ( messages[ i ].event === "CREATE_TABLE" ) { create++; } - if ( messages[ i ].event === "INSERT" ) { update++; } + }) + + dbConnector.set('table-a/item-a', 1, { val: 'aa' }, (error) => { + expect(error).to.be.null + dbConnector.get('table-a/item-a', (error, version, item) => { + expect(error).to.be.null + expect(item.val).to.equal('aa') + doneListener.emit('set-get-done', 'aa') + }) + }) + + dbConnector.set('table-a/item-b', 1, { val: 'ab' }, (error) => { + expect(error).to.be.null + dbConnector.get('table-a/item-b', (error, version, item) => { + expect(error).to.be.null + expect(item.val).to.equal('ab') + doneListener.emit('set-get-done', 'ab') + }) + }) + + dbConnector.set('table-b/item-a', 1, { val: 'ba' }, (error) => { + expect(error).to.be.null + dbConnector.get('table-b/item-a', (error, version, item) => { + expect(error).to.be.null + expect(item.val).to.equal('ba') + doneListener.emit('set-get-done', 'ba') + }) + }) + + dbConnector.set('table-b/item-b', 1, { val: 'bb' }, (error) => { + expect(error).to.be.null + dbConnector.get('table-b/item-b', (error, version, item) => { + expect(error).to.be.null + expect(item.val).to.equal('bb') + doneListener.emit('set-get-done', 'bb') + }) + }) + }) + + it('received the right notifications', () => { + expect(messages.length).to.equal(6) + var create = 0 + var update = 0 + for (var i = 0; i < messages.length; i++) { + if (messages[ i ].event === 'CREATE_TABLE') { create++ } + if (messages[ i ].event === 'INSERT') { update++ } } - expect( create ).to.equal( 2 ); - expect( update ).to.equal( 4 ); - }); - - it( "returns the right structure for the schema", done => { - dbConnector.getSchemaOverview( ( err, result ) => { - expect( err ).to.be.null; - expect( result ).to.deep.equal({ - "some-table": 2, - "some-other-table": 1, - "new-table": 1, - "table-a": 2, - "table-b": 2, - }); - done(); - }); - }); - - it( "deletes a first entry from table-a", ( done ) => { - messages = []; - dbConnector.delete( "table-a/item-a", ( err ) => { - expect( err ).to.be.null; - setTimeout( done, 300 ); - }); - }); - - it( "received an item delete notification", () => { - expect( messages.length ).to.equal( 1 ); - expect( messages[ 0 ].event ).to.equal( "DELETE" ); - messages = []; - }); - - it( "returns the right structure for the schema", done => { - dbConnector.getSchemaOverview( ( err, result ) => { - expect( err ).to.be.null; - expect( result ).to.deep.equal({ - "some-table": 2, - "some-other-table": 1, - "new-table": 1, - "table-a": 1, - "table-b": 2, - }); - done(); - }); - }); - - it( "deletes the second entry from table-a, thus triggering table deletion", ( done ) => { - messages = []; - dbConnector.delete( "table-a/item-b", ( err ) => { - expect( err ).to.be.null; - setTimeout( done, 300 ); - }); - }); - - it( "received an item delete notification", () => { - expect( messages.length ).to.equal( 2 ); - expect( messages[ 0 ].event ).to.equal( "DELETE" ); - expect( messages[ 1 ].event ).to.equal( "DESTROY_TABLE" ); - messages = []; - }); - - it( "returns the right structure for the schema", done => { - dbConnector.getSchemaOverview( ( err, result ) => { - expect( err ).to.be.null; - expect( result ).to.deep.equal({ - "some-table": 2, - "some-other-table": 1, - "new-table": 1, - "table-b": 2, - }); - done(); - }); - }); - - it( "receives notifications while still subscribed", ( done ) => { - messages = []; - dbConnector.set( "some-table/x1", 1, { val: 42 }, () => {}); + expect(create).to.equal(2) + expect(update).to.equal(4) + }) + + it('returns the right structure for the schema', done => { + dbConnector.getSchemaOverview((err, result) => { + expect(err).to.be.null + expect(result).to.deep.equal({ + 'some-table': 2, + 'some-other-table': 1, + 'new-table': 1, + 'table-a': 2, + 'table-b': 2 + }) + done() + }) + }) + + it('deletes a first entry from table-a', (done) => { + messages = [] + dbConnector.delete('table-a/item-a', (err) => { + expect(err).to.be.null + setTimeout(done, 300) + }) + }) + + it('received an item delete notification', () => { + expect(messages.length).to.equal(1) + expect(messages[ 0 ].event).to.equal('DELETE') + messages = [] + }) + + it('returns the right structure for the schema', done => { + dbConnector.getSchemaOverview((err, result) => { + expect(err).to.be.null + expect(result).to.deep.equal({ + 'some-table': 2, + 'some-other-table': 1, + 'new-table': 1, + 'table-a': 1, + 'table-b': 2 + }) + done() + }) + }) + + it('deletes the second entry from table-a, thus triggering table deletion', (done) => { + messages = [] + dbConnector.delete('table-a/item-b', (err) => { + expect(err).to.be.null + setTimeout(done, 300) + }) + }) + + it('received an item delete notification', () => { + expect(messages.length).to.equal(2) + expect(messages[ 0 ].event).to.equal('DELETE') + expect(messages[ 1 ].event).to.equal('DESTROY_TABLE') + messages = [] + }) + + it('returns the right structure for the schema', done => { + dbConnector.getSchemaOverview((err, result) => { + expect(err).to.be.null + expect(result).to.deep.equal({ + 'some-table': 2, + 'some-other-table': 1, + 'new-table': 1, + 'table-b': 2 + }) + done() + }) + }) + + it('receives notifications while still subscribed', (done) => { + messages = [] + dbConnector.set('some-table/x1', 1, { val: 42 }, () => {}) setTimeout(() => { - expect( messages.length ).to.equal( 1 ); - dbConnector.unsubscribe(( err ) => { - expect( err ).to.be.null; - done(); - }); - }, 300 ); - }); - - it( "doesn't receive notifications after unsubscribing", ( done ) => { - messages = []; - dbConnector.set( "some-table/x2", 1, { val: 43 }, ( err, result ) => { - expect( err ).to.be.null; - }); + expect(messages.length).to.equal(1) + dbConnector.unsubscribe((err) => { + expect(err).to.be.null + done() + }) + }, 300) + }) + + it("doesn't receive notifications after unsubscribing", (done) => { + messages = [] + dbConnector.set('some-table/x2', 1, { val: 43 }, (err, result) => { + expect(err).to.be.null + }) setTimeout(() => { - expect( messages.length ).to.equal( 0 ); - done(); - }, 300 ); - }); - - it( "destroys the connector", async () => { - await dbConnector.close( ); - }); - }); - - describe( "destroys databases", () => { - var dbConnector; - - it( "creates the dbConnector", ( done ) => { - dbConnector = new DbConnector( settings ); - dbConnector.init(); - expect( dbConnector.isReady ).to.equal( false ); - dbConnector.on( "ready", done ); - }); - - it( "destroys a database", ( done ) => { - dbConnector.destroySchema( "ds", ( err, result ) => { - expect( err ).to.equal( null ); - expect( result.command ).to.equal( "DROP" ); - done(); - }); - }); - - it( "fails when trying to delete a non existing database", ( done ) => { - dbConnector.destroySchema( "ds", ( err, result ) => { - expect( err.code ).to.equal( "3F000" ); //INVALID SCHEMA NAME - done(); - }); - }); - - it( "destroys the connector", done => { - dbConnector.destroy( done ); - }); - }); -}); + expect(messages.length).to.equal(0) + done() + }, 300) + }) + + it('destroys the connector', async () => { + await dbConnector.close() + }) + }) + + describe('destroys databases', () => { + var dbConnector + + it('creates the dbConnector', (done) => { + dbConnector = new DbConnector(settings) + dbConnector.init() + expect(dbConnector.isReady).to.equal(false) + dbConnector.on('ready', done) + }) + + it('destroys a database', (done) => { + dbConnector.destroySchema(settings.schema, (err, result) => { + expect(err).to.equal(null) + expect(result.command).to.equal('DROP') + done() + }) + }) + + it('fails when trying to delete a non existing database', (done) => { + dbConnector.destroySchema(settings.schema, (err, result) => { + expect(err.code).to.equal('3F000') // INVALID SCHEMA NAME + done() + }) + }) + + it('destroys the connector', done => { + dbConnector.destroy(done) + }) + }) +}) diff --git a/test/utils.spec.js b/test/utils.spec.js index 5f63992..b1313ab 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -1,86 +1,84 @@ -"use strict"; +'use strict' /* global describe, expect, it, jasmine */ -const expect = require("chai").expect; -const utils = require( "../src/utils" ); +const expect = require('chai').expect +const utils = require('../src/utils') -describe( "various utils work", () => { - - describe( "parses keys", () => { - it( "should use predefined default table name" , () => { - expect( utils.parseKey( "myKey", { schema: "bla" } ) ) +describe('various utils work', () => { + describe('parses keys', () => { + it('should use predefined default table name', () => { + expect(utils.parseKey('myKey', { schema: 'bla' })) .to.deep.equal({ - schema: "bla", - table: "default", - key: "myKey", - }); - }); + schema: 'bla', + table: 'default', + key: 'myKey' + }) + }) - it( "should use custom default table name" , () => { + it('should use custom default table name', () => { const config = { - schema: "bla", + schema: 'bla', table: { - defaultName: "customDefaultName", - }, - }; - expect( utils.parseKey( "myKey", config ) ) + defaultName: 'customDefaultName' + } + } + expect(utils.parseKey('myKey', config)) .to.deep.equal({ - schema: "bla", - table: "customDefaultName", - key: "myKey", - }); - }); + schema: 'bla', + table: 'customDefaultName', + key: 'myKey' + }) + }) - it( "should detect table name without a prefix", () => { - expect( utils.parseKey( "myTable/myKey", { schema: "bla" } ) ) + it('should detect table name without a prefix', () => { + expect(utils.parseKey('myTable/myKey', { schema: 'bla' })) .to.deep.equal({ - schema: "bla", - table: "myTable", - key: "myKey", - }); - }); + schema: 'bla', + table: 'myTable', + key: 'myKey' + }) + }) - it( "should parse multiple slashes from key", () => { - expect( utils.parseKey( "abcdefmy/key/is", { schema: "bla" } ) ) + it('should parse multiple slashes from key', () => { + expect(utils.parseKey('abcdefmy/key/is', { schema: 'bla' })) .to.deep.equal({ - schema: "bla", - table: "abcdefmy", - key: "key/is", - }); - }); + schema: 'bla', + table: 'abcdefmy', + key: 'key/is' + }) + }) - it( "should add prefix to table", () => { + it('should add prefix to table', () => { const config = { - schema: "bla", + schema: 'bla', table: { - prefix: "prefix_", - }, - }; - expect( utils.parseKey( "abcdefmy/key", config ) ) + prefix: 'prefix_' + } + } + expect(utils.parseKey('abcdefmy/key', config)) .to.deep.equal({ - schema: "bla", - table: "prefix_abcdefmy", - key: "key", - }); - }); - }); - - describe( "checks version" , () => { - it( "should parse all 9.5+ versions" , () => { - let pgnine = "PostgreSQL 9.5.9 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit"; - let pgten = "PostgreSQL 10.0 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit"; - let pgtenone = "PostgreSQL 10.0.1 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit"; - expect( utils.parsePgVersion(pgnine) ) .to.deep.equal([9, 5, 9]); - expect( utils.parsePgVersion(pgten) ) .to.deep.equal([10, 0]); - expect( utils.parsePgVersion(pgtenone) ) .to.deep.equal([10, 0, 1]); - }); + schema: 'bla', + table: 'prefix_abcdefmy', + key: 'key' + }) + }) + }) - it( "should throw if version is 9.5-" , () => { - let pgnine = "PostgreSQL 9.4.1 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit"; - expect( () => { - utils.checkVersion(pgnine); - } ) .to.throw(Error, "postgres version is 9.4.1 but minimum version is 9.5"); - }); + describe('checks version', () => { + it('should parse all 9.5+ versions', () => { + let pgnine = 'PostgreSQL 9.5.9 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit' + let pgten = 'PostgreSQL 10.0 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit' + let pgtenone = 'PostgreSQL 10.0.1 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit' + expect(utils.parsePgVersion(pgnine)).to.deep.equal([9, 5, 9]) + expect(utils.parsePgVersion(pgten)).to.deep.equal([10, 0]) + expect(utils.parsePgVersion(pgtenone)).to.deep.equal([10, 0, 1]) + }) - }); -}); + it('should throw if version is 9.5-', () => { + let pgnine = 'PostgreSQL 9.4.1 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit' + expect(() => { + utils.checkVersion(pgnine) + }).to.throw(Error, 'postgres version is 9.4.1 but minimum version is 9.5') + }) + }) +})