├── .gitignore ├── .npmignore ├── README.md ├── index.js ├── lib ├── message-dispatcher.js ├── message-factory.js ├── messages │ ├── deliver.js │ ├── hangup.js │ ├── message.js │ ├── not-supported.js │ ├── receive.js │ ├── record.js │ ├── req.js │ └── state.js ├── request.js ├── server-factory.js ├── server.js ├── sms │ ├── http-sms.js │ └── socket-sms.js └── util.js ├── package.json └── test ├── samples.js └── unit ├── fake-socket-server.js ├── message-dispatcher.js ├── message-factory.js ├── messages ├── deliver.js ├── hangup.js ├── message.js ├── not-supported.js ├── receive.js ├── record.js ├── req.js └── state.js ├── request.js ├── server-factory.js ├── server.js ├── sms ├── http-sms.js └── socket-sms.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | app.js 3 | .npm 4 | package-lock.json 5 | dev.md -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | node_modules 3 | test 4 | app.js 5 | package-lock.json 6 | dev.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This package allow to send and receive SMS messages using GSM / VOIP Goip1, Goip4, Goip8, Goip16 gateways from Hybertone / Dbltek company. SMS can be received via the UDP protocol. The SMS can be sent via UDP or HTTP. Additionally, it is possible to receive basic information about the state and status of individual GSM gates (lines). 2 | 3 | ## Installation 4 | 5 | ```bash 6 | npm install goip 7 | ``` 8 | 9 | ## Server usage instruction 10 | 11 | To start receiving messages from Goip gateways you need to create and start a server. 12 | 13 | #### Example 1 14 | 15 | ```javascript 16 | const {ServerFactory, HttpSms, SocketSms} = require('goip'); 17 | 18 | const server = ServerFactory.make(333); 19 | 20 | server.onAll( (message) => { 21 | console.log(message); 22 | }); 23 | 24 | server.run(); 25 | ``` 26 | 27 | #### Example 2 28 | 29 | ```javascript 30 | const {ServerFactory, MessageDispatcher, MessageFactory} = require('goip'); 31 | 32 | const server = ServerFactory.make(333, { 33 | 'address': '0.0.0.0', // server address 34 | 'messageDispatcher': new MessageDispatcher(), // Message Dispatcher implementation 35 | 'messageFactory': new MessageFactory() // Message Factory implementation 36 | }); 37 | 38 | server.onAll( (message) => { 39 | console.log(message); 40 | }); 41 | 42 | server.run(); 43 | ``` 44 | 45 | #### Register message listener 46 | 47 | ```javascript 48 | const {ServerFactory} = require('goip'); 49 | 50 | const server = ServerFactory.make(333); 51 | 52 | // All messages 53 | server.onAll( (message) => { 54 | console.log(message); 55 | }); 56 | 57 | // Goip not supported message 58 | server.onNotSupported( (message) => { 59 | console.log(message); 60 | }); 61 | 62 | // Keep Alive packets with gateway (line) information 63 | server.onRequest( (message) => { 64 | console.log(message); 65 | }); 66 | 67 | // Incoming SMS 68 | server.onReceive( (message) => { 69 | console.log(message); 70 | }); 71 | 72 | // SMS delivery report 73 | server.onDeliver( (message) => { 74 | console.log(message); 75 | }); 76 | 77 | // End telephone call 78 | server.onHangup( (message) => { 79 | console.log(message); 80 | }); 81 | 82 | // Start a phone call 83 | server.onRecord( (message) => { 84 | console.log(message); 85 | }); 86 | 87 | // Change of gate (line) status 88 | server.onState( (message) => { 89 | console.log(message); 90 | }); 91 | 92 | // Socket server error message 93 | server.onServerError( (message) => { 94 | console.log(message); 95 | }); 96 | 97 | server.run(); 98 | ``` 99 | 100 | ## Sending a message 101 | 102 | #### Sending via UDP socket 103 | 104 | ##### Example 1 - sending one message and close the connection 105 | 106 | ```javascript 107 | const {SocketSms} = require('goip'); 108 | 109 | const sms = new SocketSms( 110 | '192.168.0.11', // Goip address 111 | 9991, // Goip port 112 | 'goip_password' // Goip password 113 | ); 114 | 115 | sms.sendOne('999999999','test sms message').then( response => { 116 | console.log(response); 117 | }).catch(error => { 118 | console.log(error); 119 | }); 120 | ``` 121 | 122 | ##### Example 2 - sending many messages 123 | 124 | ```javascript 125 | const {SocketSms} = require('goip'); 126 | 127 | const sms = new SocketSms( 128 | '192.168.0.11', // Goip address 129 | 9991, // Goip port 130 | 'goip_password' // Goip password 131 | ); 132 | 133 | ( async () => { 134 | 135 | try { 136 | 137 | const response1 = await sms.send('999999999','test sms message 1'); 138 | const response2 = await sms.send('999999999','test sms message 2'); 139 | 140 | console.log(response1); 141 | console.log(response2); 142 | 143 | } catch (error) { 144 | 145 | console.log(error); 146 | 147 | } 148 | 149 | sms.close(); 150 | })(); 151 | ``` 152 | 153 | #### Sending via HTTP 154 | 155 | ##### Example 1 - sending one message 156 | 157 | ```javascript 158 | const {HttpSms} = require('goip'); 159 | 160 | const sms = new HttpSms( 161 | 'http://192.168.0.11', // Goip http address 162 | 1, // Line number 163 | 'login', // Goip login 164 | 'password', // Goip password 165 | { 166 | 'waitForStatus': false, // Wait and check sending status 167 | 'waitTries': 10, // Number of attempts 168 | 'waitTime': 1000 // Time in milliseconds 169 | }); 170 | 171 | sms.send('999999999', 'test message').then((response) => { 172 | console.log(response); 173 | }).catch((error) => { 174 | console.log(error); 175 | }); 176 | ``` 177 | 178 | ##### Example 2 - sending many messages 179 | 180 | ```javascript 181 | const {HttpSms} = require('goip'); 182 | 183 | const sms = new HttpSms('http://192.168.0.11',1,'login','password',{ 184 | 'waitForStatus': true 185 | }); 186 | 187 | (async () => { 188 | 189 | try { 190 | const response1 = await sms.send('999999999','test sms message 1'); 191 | const response2 = await sms.send('999999999','test sms message 2'); 192 | console.log(response1); 193 | console.log(response2); 194 | } catch (error) { 195 | console.log(error); 196 | } 197 | 198 | })(); 199 | ``` 200 | 201 | ##### Example 3 - checking status by id 202 | 203 | ```javascript 204 | const {HttpSms} = require('goip'); 205 | const sms = new HttpSms( 206 | 'http://192.168.0.11', // Goip http address 207 | 1, // Line number 208 | 'login', // Goip login 209 | 'password', // Goip password 210 | ); 211 | 212 | ( async () => { 213 | 214 | try { 215 | const response = await sms.send('999999999', 'test message'); 216 | 217 | console.log(response); 218 | 219 | setTimeout(async () => { 220 | const isSend = await sms.isSend( response.id ); 221 | 222 | if( isSend ) { 223 | console.log('sms sent') 224 | } 225 | 226 | if( !isSend ) { 227 | console.log('sms not sent') 228 | } 229 | 230 | }, 5000); 231 | 232 | } catch (error) { 233 | console.log(error); 234 | } 235 | 236 | })(); 237 | ``` 238 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'ServerFactory': require('./lib/server-factory'), 3 | 'HttpSms': require('./lib/sms/http-sms'), 4 | 'SocketSms': require('./lib/sms/socket-sms'), 5 | 'MessageDispatcher': require('./lib/message-dispatcher'), 6 | 'MessageFactory': require('./lib/message-factory'), 7 | } -------------------------------------------------------------------------------- /lib/message-dispatcher.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const EventEmiiter = require('events').EventEmitter; 3 | 4 | module.exports = class MessageDispatcher extends EventEmiiter { 5 | 6 | dispatch(type, message) { 7 | this.emit(type, message); 8 | } 9 | 10 | listen(message, callback) { 11 | this.on(message, callback); 12 | } 13 | 14 | }; -------------------------------------------------------------------------------- /lib/message-factory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const ReqMessage = require('./messages/req'); 4 | const ReceiveMessage = require('./messages/receive') 5 | const NotSupportedMessage = require('./messages/not-supported'); 6 | const DeliverMessage = require('./messages/deliver'); 7 | const HangupMessage = require('./messages/hangup') 8 | const RecordMessage = require('./messages/record') 9 | const StateMessage = require('./messages/state'); 10 | 11 | module.exports = class MessageFactory { 12 | 13 | constructor() { 14 | this._messages = { 15 | 'req': ReqMessage, 16 | 'receive': ReceiveMessage, 17 | 'deliver': DeliverMessage, 18 | 'hangup': HangupMessage, 19 | 'record': RecordMessage, 20 | 'state': StateMessage 21 | }; 22 | } 23 | 24 | make( request ) { 25 | const type = Object.keys(request.all())[0]; 26 | 27 | if( this._messages.hasOwnProperty(type) ) { 28 | return new this._messages[ type ]( request ); 29 | } 30 | 31 | return new NotSupportedMessage(request); 32 | } 33 | } -------------------------------------------------------------------------------- /lib/messages/deliver.js: -------------------------------------------------------------------------------- 1 | const Message = require('./message'); 2 | 3 | module.exports = class Deliver extends Message { 4 | 5 | ack() { 6 | return 'DELIVER ' + this.deliver() + ' OK'; 7 | } 8 | 9 | deliver() { 10 | return this.request().get('deliver'); 11 | } 12 | 13 | smsNo() { 14 | return this.request().get('sms_no'); 15 | } 16 | 17 | state() { 18 | return this.request().get('state'); 19 | } 20 | 21 | num() { 22 | return this.request().get('num'); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /lib/messages/hangup.js: -------------------------------------------------------------------------------- 1 | const Message = require('./message'); 2 | 3 | module.exports = class Hengup extends Message { 4 | 5 | ack() { 6 | return 'HANGUP ' + this.hangup() + ' OK'; 7 | } 8 | 9 | hangup() { 10 | return this.request().get('hangup'); 11 | } 12 | 13 | num() { 14 | return this.request().get('num'); 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /lib/messages/message.js: -------------------------------------------------------------------------------- 1 | module.exports = class Message { 2 | 3 | constructor( request ) { 4 | this._request = request; 5 | } 6 | 7 | attributes() 8 | { 9 | return this._request.all(); 10 | } 11 | 12 | request() { 13 | return this._request; 14 | } 15 | 16 | id() { 17 | return this._request.get('id'); 18 | } 19 | 20 | password() { 21 | return this._request.get('password'); 22 | } 23 | 24 | ack() { 25 | return null; 26 | } 27 | } -------------------------------------------------------------------------------- /lib/messages/not-supported.js: -------------------------------------------------------------------------------- 1 | const Message = require('./message'); 2 | 3 | module.exports = class NotSupported extends Message { 4 | 5 | } -------------------------------------------------------------------------------- /lib/messages/receive.js: -------------------------------------------------------------------------------- 1 | const Message = require('./message'); 2 | 3 | module.exports = class Receive extends Message { 4 | 5 | ack() { 6 | return 'RECEIVE ' + this.receive() + ' OK'; 7 | } 8 | 9 | receive() { 10 | return this.request().get('receive'); 11 | } 12 | 13 | srcnum() { 14 | return this.request().get('srcnum'); 15 | } 16 | 17 | msg() { 18 | return this.request().get('msg'); 19 | } 20 | } -------------------------------------------------------------------------------- /lib/messages/record.js: -------------------------------------------------------------------------------- 1 | const Message = require('./message'); 2 | 3 | module.exports = class Record extends Message { 4 | 5 | ack() { 6 | return 'RECORD ' + this.record() + ' OK'; 7 | } 8 | 9 | record() { 10 | return this.request().get('record'); 11 | } 12 | 13 | dir() { 14 | return this.request().get('dir'); 15 | } 16 | 17 | 18 | num() { 19 | return this.request().get('num'); 20 | } 21 | 22 | 23 | } -------------------------------------------------------------------------------- /lib/messages/req.js: -------------------------------------------------------------------------------- 1 | const Message = require('./message'); 2 | 3 | module.exports = class Req extends Message { 4 | 5 | ack() { 6 | return 'req:' + this.req() + ';status:200;'; 7 | } 8 | 9 | req() { 10 | return this.request().get('req'); 11 | } 12 | 13 | password() { 14 | return this.request().get('pass'); 15 | } 16 | 17 | num() { 18 | return this.request().get('num'); 19 | } 20 | 21 | signal() { 22 | return this.request().get('signal'); 23 | } 24 | 25 | gsmStatus() { 26 | return this.request().get('gsm_status'); 27 | } 28 | 29 | voipStatus() { 30 | return this.request().get('voip_status'); 31 | } 32 | 33 | voipState() { 34 | return this.request().get('voip_state'); 35 | } 36 | 37 | remainTime() { 38 | return this.request().get('remain_time'); 39 | } 40 | 41 | imei() { 42 | return this.request().get('imei'); 43 | } 44 | 45 | 46 | pro() { 47 | return this.request().get('pro'); 48 | } 49 | 50 | idle() { 51 | return this.request().get('idle'); 52 | } 53 | 54 | disableStatus() { 55 | return this.request().get('disable_status'); 56 | } 57 | 58 | smsLogin() { 59 | return this.request().get('sms_login'); 60 | } 61 | 62 | smbLogin() { 63 | return this.request().get('smb_login'); 64 | } 65 | 66 | cellinfo() { 67 | return this.request().get('cellinfo'); 68 | } 69 | 70 | cgatt() { 71 | return this.request().get('cgatt'); 72 | } 73 | } -------------------------------------------------------------------------------- /lib/messages/state.js: -------------------------------------------------------------------------------- 1 | const Message = require('./message'); 2 | 3 | module.exports = class State extends Message { 4 | 5 | ack() { 6 | return 'STATE ' + this.state() + ' OK'; 7 | } 8 | 9 | state() { 10 | return this.request().get('state'); 11 | } 12 | 13 | gsmRemainState() 14 | { 15 | return this.request().get('gsm_remain_state'); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /lib/request.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = class Request { 3 | 4 | constructor(buffer, address, port) { 5 | this._buffer = buffer; 6 | this._address = address; 7 | this._port = port; 8 | this._attributes = this._parse(buffer); 9 | } 10 | 11 | all() { 12 | return this._attributes; 13 | } 14 | 15 | buffer(){ 16 | return this._buffer; 17 | } 18 | 19 | port() { 20 | return this._port; 21 | } 22 | 23 | address() { 24 | return this._address; 25 | } 26 | 27 | has( key ) { 28 | return this._attributes.hasOwnProperty(key); 29 | } 30 | 31 | get( key ) { 32 | return this.has( key ) ? this._attributes[ key ] : null; 33 | } 34 | 35 | _parse(buffer) { 36 | let attributes = {}; 37 | let arr = buffer.toLowerCase().split(';'); 38 | for( let i = 0; i < arr.length; i++ ) { 39 | let parts = arr[i].split(':'); 40 | let key = parts.shift(); 41 | let val = parts.join(':'); 42 | 43 | if( !key.length ) { 44 | continue; 45 | } 46 | 47 | attributes[ key ] = val; 48 | } 49 | 50 | return attributes; 51 | } 52 | } -------------------------------------------------------------------------------- /lib/server-factory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const MessageDispatcher = require('./message-dispatcher'); 4 | const MessageFactory = require('./message-factory'); 5 | const Server = require('./server'); 6 | 7 | module.exports = class ServerFactory { 8 | static make( port, {address, messageFactory, messageDispatcher} = {}) { 9 | 10 | const messageFactoryInstance = messageFactory || new MessageFactory(); 11 | const messageDispatcherInstance = messageDispatcher || new MessageDispatcher(); 12 | return new Server({ 13 | 'port': port, 14 | 'address': address, 15 | 'messageFactory': messageFactoryInstance, 16 | 'messageDispatcher': messageDispatcherInstance 17 | }); 18 | } 19 | } -------------------------------------------------------------------------------- /lib/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const dgram = require('dgram'); 4 | const GoipRequest = require('./request'); 5 | 6 | module.exports = class Server { 7 | 8 | constructor({port, address, messageFactory, messageDispatcher}) { 9 | this._port = port; 10 | this._address = address; 11 | this._messageFactory = messageFactory; 12 | this._messageDispatcher = messageDispatcher; 13 | this._socket = dgram.createSocket('udp4'); 14 | } 15 | 16 | _send(msg, address, port) { 17 | this._socket.send(msg, 0, msg.length, port, address ) 18 | } 19 | 20 | run() { 21 | 22 | this._socket.on('error',this._errorHandler.bind(this)); 23 | 24 | this._socket.on('message', this._messageHandler.bind(this)); 25 | 26 | this._socket.on('listening', this._listeningHandler.bind(this)); 27 | 28 | this._socket.bind(this._port, this._address); 29 | } 30 | 31 | _messageHandler(msg, rinfo) { 32 | const req = this._makeGoipRequest(msg,rinfo); 33 | const message = this._messageFactory.make( req ); 34 | 35 | if( message.ack() ) { 36 | this._send( message.ack(), rinfo.address, rinfo.port ); 37 | } 38 | 39 | this._messageDispatcher.dispatch( 'message', message ); 40 | this._messageDispatcher.dispatch( message.constructor.name.toLowerCase(), message ); 41 | } 42 | 43 | _errorHandler() { 44 | this.close(); 45 | } 46 | 47 | _listeningHandler() { 48 | const address = this._socket.address(); 49 | console.log(`server listening ${address.address}:${address.port}`); 50 | } 51 | 52 | _makeGoipRequest(msg,rinfo) { 53 | return new GoipRequest(msg.toString('utf-8'),rinfo.address,rinfo.port); 54 | } 55 | 56 | close() { 57 | this._socket.close() 58 | } 59 | 60 | onMessage( message, callback ) { 61 | this._messageDispatcher.listen(message,callback); 62 | } 63 | 64 | onAll( callback ) { 65 | this.onMessage('message', callback) 66 | } 67 | 68 | onRequest( callback ) { 69 | this.onMessage( 'request', callback ); 70 | } 71 | 72 | onNotSupported( callback ) { 73 | this.onMessage( 'notsupported', callback ); 74 | } 75 | 76 | onReceive( callback ) { 77 | this.onMessage( 'receive', callback ); 78 | } 79 | 80 | onDeliver( callback ) { 81 | this.onMessage( 'deliver', callback ); 82 | } 83 | 84 | onHangup( callback ) { 85 | this.onMessage( 'hangup', callback ); 86 | } 87 | 88 | onRecord( callback ) { 89 | this.onMessage( 'record', callback ); 90 | } 91 | 92 | onState( callback ) { 93 | this.onMessage( 'state', callback ); 94 | } 95 | 96 | onServerError( callback ) { 97 | this._socket.on('error', callback); 98 | } 99 | 100 | onServerListening( callback ) { 101 | this._socket.on('listening', callback); 102 | } 103 | }; -------------------------------------------------------------------------------- /lib/sms/http-sms.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const xmlParser = require('fast-xml-parser'); 3 | 4 | module.exports = class HttpSms { 5 | 6 | constructor( url, line, login, password, options ) { 7 | 8 | this._url = this._prepareUrl(url); 9 | this._line = line; 10 | this._login = login; 11 | this._password = password; 12 | 13 | const defaults = { 14 | 'sendDir': '/default/en_US/send.html', 15 | 'statusDir': '/default/en_US/send_status.xml', 16 | 'waitForStatus': false, 17 | 'waitTries': 10, 18 | 'waitTime': 1000 19 | }; 20 | 21 | this._options = Object.assign(defaults, options); 22 | } 23 | 24 | async send(number, message) { 25 | 26 | const params = new URLSearchParams(); 27 | params.append('u', this._login); 28 | params.append('p', this._password); 29 | params.append('l', this._line); 30 | params.append('n', number); 31 | params.append('m', message); 32 | 33 | const response = await fetch(this._url + this._options.sendDir + '?' + params); 34 | const data = await response.text(); 35 | 36 | const parsedData = this._parse(data); 37 | 38 | if( this._options.waitForStatus ) { 39 | let isSendStatus = false; 40 | for( let i = 0; i < this._options.waitTries; i++ ) 41 | { 42 | try { 43 | isSendStatus = await this._waitForStatus( parsedData.id ); 44 | } catch (error) { 45 | parsedData.status = 'error'; 46 | parsedData.error = error.message; 47 | break; 48 | } 49 | 50 | 51 | if( isSendStatus ) { 52 | parsedData.status = 'send'; 53 | break; 54 | } 55 | } 56 | } 57 | 58 | return parsedData; 59 | } 60 | 61 | _waitForStatus(id) { 62 | return new Promise( (resolve) => { 63 | setTimeout( () => { 64 | resolve( this.isSend( id ) ); 65 | }, this._options.waitTime) 66 | }); 67 | } 68 | 69 | async isSend( id ) { 70 | 71 | const params = new URLSearchParams(); 72 | params.append('u', this._login); 73 | params.append('p', this._password); 74 | 75 | const response = await fetch(this._url + this._options.statusDir + '?' + params); 76 | const data = await response.text(); 77 | 78 | const xml = xmlParser.parse(data); 79 | const statusList = xml['send-sms-status']; 80 | 81 | if( ! statusList ) { 82 | throw new Error('Sms send status not found!'); 83 | } 84 | 85 | const smsId = statusList['id'+this._line]; 86 | const status = statusList['status'+this._line]; 87 | const error = statusList['error'+this._line]; 88 | 89 | if( !smsId || smsId !== id ) { 90 | return false; 91 | } 92 | 93 | if( error ) 94 | { 95 | throw new Error(error); 96 | } 97 | 98 | if( status.toLowerCase() === 'done' ) { 99 | return true; 100 | } 101 | 102 | return false; 103 | 104 | } 105 | 106 | _parse( response ) { 107 | 108 | const res = response.trim().toLowerCase(); 109 | 110 | if( res.includes('error') || !res.includes('sending') ) { 111 | throw new Error(response) 112 | } 113 | 114 | const arr = res.split(';'); 115 | 116 | if( !arr[arr.length -1 ].includes('id') ) { 117 | throw new Error("Sms id not found in respons"); 118 | } 119 | 120 | const id = arr[arr.length -1 ].split(':')[1]; 121 | 122 | return { 123 | 'id': id, 124 | 'raw': response.trim(), 125 | 'status': 'sending' 126 | } 127 | } 128 | 129 | _prepareUrl( url ) { 130 | return ( url.substr(url.length-1) === '/' ) ? url.substr(0,url.length-1) : url; 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /lib/sms/socket-sms.js: -------------------------------------------------------------------------------- 1 | const dgram = require('dgram'); 2 | const {randomInt} = require('../util'); 3 | 4 | module.exports = class SocketSms { 5 | 6 | constructor(address,port,password, timeout = 8000) { 7 | 8 | this._requestStatus = { 9 | error: 0, 10 | success: 2, 11 | }; 12 | 13 | this._address = address; 14 | this._port = port; 15 | this._id = null; 16 | this._password = password; 17 | this._socket = dgram.createSocket('udp4'); 18 | this._pendingRequest = null; 19 | this._expectedResponse = null; 20 | this._timeoutHandler = null; 21 | this._timeout = timeout; 22 | 23 | this._socket.on('message', (msg) => { 24 | let utf8msg = msg.toString('utf-8'); 25 | 26 | if( utf8msg && this._checkResponse( utf8msg, 'ERROR', this._id) ) { 27 | 28 | this._socket.emit('expected-' + this._expectedResponse, { 29 | 'status': this._requestStatus.error, 30 | 'error': new Error(this._pendingRequest + ': ' + utf8msg) 31 | }); 32 | 33 | this.close(); 34 | } 35 | 36 | if( utf8msg && this._checkResponse(utf8msg, this._expectedResponse, this._id) ) { 37 | 38 | this._socket.emit('expected-' + this._expectedResponse, { 39 | 'status': this._requestStatus.success, 40 | 'message': utf8msg 41 | }); 42 | } 43 | }); 44 | } 45 | 46 | _isTimeout ( reject ) { 47 | this._timeoutHandler = setTimeout(() => { 48 | reject( new Error('Timeout in ' + this._pendingRequest) ) 49 | this.close() 50 | }, this._timeout) 51 | } 52 | 53 | _checkResponse( msg, expected, id ) { 54 | return msg.substr(0, ( 1 + expected.length + id.toString().length ) ) === expected + ' ' + id.toString(); 55 | } 56 | 57 | async send(number,message,id) { 58 | 59 | this._id = id || randomInt(10000,99999); 60 | 61 | const bulk = await this._sendBulkSmsRequest(message,this._id); 62 | const auth = await this._sendAuthRequest(this._password, this._id); 63 | const send = await this._sendNumberRequest(number,this._id); 64 | const end = await this._sendEndRequest(this._id); 65 | 66 | const prepare = send.trim().split(' '); 67 | 68 | return { 69 | 'sendid': prepare[1], // bulk SMS session identifier 70 | 'telid': prepare[2], // Integer, unique sequence number in SubmitNumberRequest. 71 | 'sms_no': prepare[3], // number count of SMS sending in GoIP 72 | 'raw': send.trim() 73 | }; 74 | } 75 | 76 | async sendOne(number,message,id) { 77 | const send = await this.send(number,message,id); 78 | this.close(); 79 | return send; 80 | } 81 | 82 | close() { 83 | this._socket.close(); 84 | } 85 | 86 | _sendRequest(msg, expected, request) { 87 | this._expectedResponse = expected; 88 | this._pendingRequest = request; 89 | 90 | return new Promise( (resolve, reject) => { 91 | const message = msg + '\n'; 92 | this._socket.once('expected-' + expected, (res) => { 93 | clearTimeout(this._timeoutHandler); 94 | if( res.status === this._requestStatus.error ) { 95 | reject( res.error ); 96 | } else { 97 | resolve( res.message ) 98 | } 99 | }); 100 | this._socket.send(message, 0, message.length, this._port, this._address ); 101 | this._isTimeout(reject); 102 | }); 103 | } 104 | 105 | _sendBulkSmsRequest( message, id ) { 106 | const cutmessage = message.substr(0,3000) 107 | const msg = 'MSG ' + id + ' ' + cutmessage.length + ' ' + cutmessage; 108 | return this._sendRequest(msg, 'PASSWORD', 'sendBulkSmsRequest'); 109 | } 110 | 111 | _sendAuthRequest(password,id) { 112 | const msg = 'PASSWORD ' + id + ' ' + password; 113 | return this._sendRequest(msg, 'SEND', 'sendAuthRequest'); 114 | } 115 | 116 | _sendNumberRequest( number, id ) { 117 | const msg = 'SEND ' + id + ' 1 ' + number; 118 | return this._sendRequest(msg, 'OK', 'sendNumberRequest'); 119 | } 120 | 121 | _sendEndRequest(id) { 122 | const msg = 'DONE ' + id; 123 | return this._sendRequest(msg, 'DONE', 'sendEndRequest'); 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.randomInt = (min,max) => { 4 | return Math.floor(Math.random() * (max - min) + min); 5 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goip", 3 | "version": "0.2.0", 4 | "description": "Simple server and client for goip gateways", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha ./test/ --recursive" 8 | }, 9 | "keywords": [ 10 | "goip", 11 | "sms", 12 | "server", 13 | "client" 14 | ], 15 | "author": "Michal Styrylski", 16 | "license": "MIT", 17 | "dependencies": { 18 | "fast-xml-parser": "^3.16.0", 19 | "node-fetch": "^2.6.0" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git://github.com/styryl/node-goip.git" 24 | }, 25 | "devDependencies": { 26 | "chai": "^4.2.0", 27 | "mocha": "^7.1.1", 28 | "nock": "^12.0.3", 29 | "proxyquire": "^2.1.3", 30 | "sinon": "^9.0.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/samples.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'buffers': { 3 | 'req': 'req:27;id:gateway_name;pass:gateway_password;num:;signal:29;gsm_status:LOGIN;voip_status:LOGOUT;voip_state:IDLE;remain_time:-1;imei:867495023958030;imsi:260060172091324;iccid:89480611000720913242;pro:POL;idle:11;disable_status:0;SMS_LOGIN:N;SMB_LOGIN:;CELLINFO:LAC:7E5,CELL ID:5E5B;CGATT:Y;', 4 | 'fake': 'key1:val1;key2:val2;key3:val3;' 5 | }, 6 | 'xmlDone': ` 7 | 8 | 0000006d 9 | DONE 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | `, 33 | 'xmlError': ` 34 | 35 | 0000006d 36 | Error 37 | error message 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | `, 60 | 'xmlStartingStatus': ` 61 | 62 | 0000006d 63 | startign 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | ` 87 | }; -------------------------------------------------------------------------------- /test/unit/fake-socket-server.js: -------------------------------------------------------------------------------- 1 | const Socket = require('dgram').Socket; 2 | 3 | module.exports = class FakeSocketServer extends Socket { 4 | 5 | close() {} 6 | 7 | send(msg,offset,length,port,address) { 8 | const message = msg.trim(); 9 | const id = message.split(' ')[1]; 10 | 11 | if( message === 'test message' ) { 12 | this.emit('expected-test-message', { 13 | 'status': 2, 14 | 'message': 'test message ok' 15 | }); 16 | }; 17 | 18 | if( message === 'test error' ) { 19 | this.emit('expected-test-error', { 20 | 'status': 0, 21 | 'error': new Error('test error') 22 | }); 23 | }; 24 | 25 | if( message.includes('MSG') ) { 26 | this.emit('expected-PASSWORD', { 27 | 'status': 2, 28 | 'message': 'PASSWORD ' + id 29 | }); 30 | }; 31 | 32 | if( message.includes('PASSWORD') ) { 33 | this.emit('expected-SEND', { 34 | 'status': 2, 35 | 'message': 'SEND ' + id 36 | }); 37 | }; 38 | 39 | if( message.includes('SEND') ) { 40 | this.emit('expected-OK', { 41 | 'status': 2, 42 | 'message': 'OK '+id+' 1 11' 43 | }); 44 | }; 45 | 46 | if( message.includes('DONE') ) { 47 | this.emit('expected-DONE', { 48 | 'status': 2, 49 | 'message': 'DONE ' + id 50 | }); 51 | }; 52 | } 53 | } -------------------------------------------------------------------------------- /test/unit/message-dispatcher.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const MessageDispatcher = require('../../lib/message-dispatcher'); 3 | const Message = require('../../lib/messages/message'); 4 | 5 | const sinon = require('sinon'); 6 | 7 | describe('MessageDispatcherTest', () => { 8 | 9 | const messageDispatcher = new MessageDispatcher(); 10 | 11 | describe('#listen()', () => { 12 | 13 | it('should register message listener', () => { 14 | const spyListen = sinon.spy(messageDispatcher, 'listen'); 15 | const callback = sinon.spy(); 16 | 17 | messageDispatcher.listen('message',callback); 18 | 19 | expect( spyListen.withArgs('message',callback).calledOnce ).to.be.true; 20 | }); 21 | 22 | }); 23 | 24 | describe('#dispatch()', () => { 25 | 26 | it('should dispatch message by type', () => { 27 | 28 | const spyDispatch = sinon.spy(messageDispatcher, 'dispatch'); 29 | const callback = sinon.spy(); 30 | const messageStub = sinon.createStubInstance(Message); 31 | 32 | messageDispatcher.listen('message', callback) 33 | 34 | messageDispatcher.dispatch('message', messageStub); 35 | 36 | expect( spyDispatch.withArgs('message',messageStub).calledOnce ).to.be.true; 37 | expect( callback.withArgs(messageStub).calledOnce ).to.be.true; 38 | }); 39 | 40 | }); 41 | 42 | 43 | 44 | }); -------------------------------------------------------------------------------- /test/unit/message-factory.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const MessageFactory = require('../../lib/message-factory'); 3 | const Message = require('../../lib/messages/message'); 4 | const NotSupportedMessage = require('../../lib/messages/not-supported'); 5 | const ReqMessage = require('../../lib/messages/req'); 6 | const DeliverMessage = require('../../lib/messages/deliver'); 7 | const HangupMessage = require('../../lib/messages/hangup'); 8 | const ReceiveMessage = require('../../lib/messages/receive'); 9 | const RecordMessage = require('../../lib/messages/record'); 10 | const StateMessage = require('../../lib/messages/state'); 11 | const Request = require('../../lib/request'); 12 | 13 | const sinon = require('sinon'); 14 | 15 | describe('MessageFactoryTest', () => { 16 | 17 | const messageFactory = new MessageFactory(); 18 | 19 | describe('#constructor()', () => { 20 | it('should be created with _messages property', () => { 21 | expect(messageFactory).to.have.property('_messages'); 22 | }); 23 | }); 24 | 25 | describe('#make()', () => { 26 | 27 | it('can create NotSupported Message', () => { 28 | 29 | const requestStub = sinon.createStubInstance(Request); 30 | requestStub.all.returns({ 31 | 'test':'1' 32 | }); 33 | 34 | const message = messageFactory.make(requestStub); 35 | expect(message).to.be.instanceOf(Message); 36 | expect(message).to.be.instanceOf(NotSupportedMessage); 37 | expect(requestStub.all.calledOnce).to.be.true; 38 | }); 39 | 40 | it('can create Req Message', () => { 41 | 42 | const requestStub = sinon.createStubInstance(Request); 43 | requestStub.all.returns({ 44 | 'req':'1' 45 | }); 46 | 47 | const message = messageFactory.make(requestStub); 48 | expect(message).to.be.instanceOf(Message); 49 | expect(message).to.be.instanceOf(ReqMessage); 50 | expect(requestStub.all.calledOnce).to.be.true; 51 | }); 52 | 53 | it('can create Deliver Message', () => { 54 | 55 | const requestStub = sinon.createStubInstance(Request); 56 | requestStub.all.returns({ 57 | 'deliver':'1' 58 | }); 59 | 60 | const message = messageFactory.make(requestStub); 61 | expect(message).to.be.instanceOf(Message); 62 | expect(message).to.be.instanceOf(DeliverMessage); 63 | expect(requestStub.all.calledOnce).to.be.true; 64 | }); 65 | 66 | it('can create Hangup Message', () => { 67 | 68 | const requestStub = sinon.createStubInstance(Request); 69 | requestStub.all.returns({ 70 | 'hangup':'1' 71 | }); 72 | 73 | const message = messageFactory.make(requestStub); 74 | expect(message).to.be.instanceOf(Message); 75 | expect(message).to.be.instanceOf(HangupMessage); 76 | expect(requestStub.all.calledOnce).to.be.true; 77 | }); 78 | 79 | it('can create Receive Message', () => { 80 | 81 | const requestStub = sinon.createStubInstance(Request); 82 | requestStub.all.returns({ 83 | 'receive':'1' 84 | }); 85 | 86 | const message = messageFactory.make(requestStub); 87 | expect(message).to.be.instanceOf(Message); 88 | expect(message).to.be.instanceOf(ReceiveMessage); 89 | expect(requestStub.all.calledOnce).to.be.true; 90 | }); 91 | 92 | it('can create Record Message', () => { 93 | 94 | const requestStub = sinon.createStubInstance(Request); 95 | requestStub.all.returns({ 96 | 'record':'1' 97 | }); 98 | 99 | const message = messageFactory.make(requestStub); 100 | expect(message).to.be.instanceOf(Message); 101 | expect(message).to.be.instanceOf(RecordMessage); 102 | expect(requestStub.all.calledOnce).to.be.true; 103 | }); 104 | 105 | it('can create State Message', () => { 106 | 107 | const requestStub = sinon.createStubInstance(Request); 108 | requestStub.all.returns({ 109 | 'state':'1' 110 | }); 111 | 112 | const message = messageFactory.make(requestStub); 113 | expect(message).to.be.instanceOf(Message); 114 | expect(message).to.be.instanceOf(StateMessage); 115 | expect(requestStub.all.calledOnce).to.be.true; 116 | }); 117 | 118 | }); 119 | 120 | 121 | }); -------------------------------------------------------------------------------- /test/unit/messages/deliver.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const sinon = require('sinon'); 3 | const Message = require('../../../lib/messages/message'); 4 | const DeliverMessage = require('../../../lib/messages/deliver'); 5 | const Request = require('../../../lib/request'); 6 | 7 | describe('DeliverTest', function() { 8 | 9 | const requestStub = sinon.createStubInstance(Request); 10 | let message; 11 | 12 | beforeEach(()=>{ 13 | message = new DeliverMessage( requestStub ); 14 | }); 15 | 16 | describe('#constructor()', () => { 17 | 18 | it('should be instance of Message', () => { 19 | expect(message).to.be.instanceOf(Message); 20 | }); 21 | 22 | it('should be create with properties', () => { 23 | expect(message).to.have.property('_request'); 24 | }); 25 | 26 | it('_request property should be instance of Request', () => { 27 | expect(message._request).to.be.instanceOf(Request); 28 | }); 29 | 30 | }); 31 | 32 | describe('#attributes()', function() { 33 | 34 | it('should return all attributes from request all method', () => { 35 | requestStub.all.returns({req:1}); 36 | expect(message.attributes()).to.be.deep.equal({req:1}); 37 | }); 38 | 39 | }); 40 | 41 | describe('#request()', () => { 42 | 43 | it('should return request', () => { 44 | expect(message.request()).to.be.instanceOf(Request); 45 | }); 46 | 47 | }); 48 | 49 | describe('#id()', () => { 50 | 51 | it('should return id from request get method', () => { 52 | requestStub.get.withArgs('id').returns(1); 53 | expect(message.id()).to.be.deep.equal(1); 54 | }); 55 | 56 | }); 57 | 58 | describe('#password()', () => { 59 | 60 | it('should return password from request get method', () => { 61 | requestStub.get.withArgs('password').returns('pass'); 62 | expect(message.password()).to.be.equal('pass'); 63 | }); 64 | 65 | }); 66 | 67 | describe('#ack()', () => { 68 | it('should return ack message', () => { 69 | requestStub.get.withArgs('deliver').returns('123'); 70 | expect(message.ack()).to.be.equal('DELIVER 123 OK'); 71 | }); 72 | }); 73 | 74 | describe('#deliver()', () => { 75 | it('should return deliver count', () => { 76 | requestStub.get.withArgs('deliver').returns('123'); 77 | expect(message.deliver()).to.be.equal('123'); 78 | }); 79 | }); 80 | 81 | describe('#smsNo()', () => { 82 | it('should return sms no count', () => { 83 | requestStub.get.withArgs('sms_no').returns('22'); 84 | expect(message.smsNo()).to.be.equal('22'); 85 | }); 86 | }); 87 | 88 | describe('#state()', () => { 89 | it('should return state', () => { 90 | requestStub.get.withArgs('state').returns('test state'); 91 | expect(message.state()).to.be.equal('test state'); 92 | }); 93 | }); 94 | 95 | describe('#num()', () => { 96 | it('should return phone number', () => { 97 | requestStub.get.withArgs('num').returns('999999999'); 98 | expect(message.num()).to.be.equal('999999999'); 99 | }); 100 | }); 101 | 102 | }); -------------------------------------------------------------------------------- /test/unit/messages/hangup.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const sinon = require('sinon'); 3 | const Message = require('../../../lib/messages/message'); 4 | const HangupMessage = require('../../../lib/messages/hangup'); 5 | const Request = require('../../../lib/request'); 6 | 7 | describe('HangupTest', function() { 8 | 9 | const requestStub = sinon.createStubInstance(Request); 10 | let message; 11 | 12 | beforeEach(()=>{ 13 | message = new HangupMessage( requestStub ); 14 | }); 15 | 16 | describe('#constructor()', () => { 17 | 18 | it('should be instance of Message', () => { 19 | expect(message).to.be.instanceOf(Message); 20 | }); 21 | 22 | it('should be create with properties', () => { 23 | expect(message).to.have.property('_request'); 24 | }); 25 | 26 | it('_request property should be instance of Request', () => { 27 | expect(message._request).to.be.instanceOf(Request); 28 | }); 29 | 30 | }); 31 | 32 | describe('#attributes()', function() { 33 | 34 | it('should return all attributes from request all method', () => { 35 | requestStub.all.returns({req:1}); 36 | expect(message.attributes()).to.be.deep.equal({req:1}); 37 | }); 38 | 39 | }); 40 | 41 | describe('#request()', () => { 42 | 43 | it('should return request', () => { 44 | expect(message.request()).to.be.instanceOf(Request); 45 | }); 46 | 47 | }); 48 | 49 | describe('#id()', () => { 50 | 51 | it('should return id from request get method', () => { 52 | requestStub.get.withArgs('id').returns(1); 53 | expect(message.id()).to.be.deep.equal(1); 54 | }); 55 | 56 | }); 57 | 58 | describe('#password()', () => { 59 | 60 | it('should return password from request get method', () => { 61 | requestStub.get.withArgs('password').returns('pass'); 62 | expect(message.password()).to.be.equal('pass'); 63 | }); 64 | 65 | }); 66 | 67 | describe('#ack()', () => { 68 | it('should return ack message', () => { 69 | requestStub.get.withArgs('hangup').returns('123'); 70 | expect(message.ack()).to.be.equal('HANGUP 123 OK'); 71 | }); 72 | }); 73 | 74 | describe('#hangup()', () => { 75 | it('should return hangup count', () => { 76 | requestStub.get.withArgs('hangup').returns('123'); 77 | expect(message.hangup()).to.be.equal('123'); 78 | }); 79 | }); 80 | 81 | describe('#num()', () => { 82 | it('should return phone number', () => { 83 | requestStub.get.withArgs('num').returns('999999999'); 84 | expect(message.num()).to.be.equal('999999999'); 85 | }); 86 | }); 87 | 88 | }); -------------------------------------------------------------------------------- /test/unit/messages/message.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const sinon = require('sinon'); 3 | const Message = require('../../../lib/messages/message'); 4 | const Request = require('../../../lib/request'); 5 | const samples = require('../../samples'); 6 | 7 | describe('MessageTest', function() { 8 | 9 | const requestStub = sinon.createStubInstance(Request); 10 | let message; 11 | 12 | beforeEach(()=>{ 13 | message = new Message( requestStub ); 14 | }); 15 | 16 | describe('#constructor()', () => { 17 | 18 | it('should be create with properties', () => { 19 | expect(message).to.have.property('_request'); 20 | }); 21 | 22 | it('_request property should be instance of Request', () => { 23 | expect(message._request).to.be.instanceOf(Request); 24 | }); 25 | 26 | }); 27 | 28 | describe('#attributes()', function() { 29 | 30 | it('should return all attributes from request all method', () => { 31 | requestStub.all.returns({req:1}); 32 | expect(message.attributes()).to.be.deep.equal({req:1}); 33 | }); 34 | 35 | }); 36 | 37 | describe('#request()', () => { 38 | 39 | it('should return request', () => { 40 | expect(message.request()).to.be.instanceOf(Request); 41 | }); 42 | 43 | }); 44 | 45 | describe('#id()', () => { 46 | 47 | it('should return id from request get method', () => { 48 | requestStub.get.withArgs('id').returns(1); 49 | expect(message.id()).to.be.deep.equal(1); 50 | }); 51 | 52 | }); 53 | 54 | describe('#password()', () => { 55 | 56 | it('should return password from request get method', () => { 57 | requestStub.get.withArgs('password').returns('pass'); 58 | expect(message.password()).to.be.equal('pass'); 59 | }); 60 | 61 | }); 62 | 63 | describe('#ack()', () => { 64 | 65 | it('should return null', () => { 66 | expect(message.ack()).to.be.null; 67 | }); 68 | }); 69 | 70 | }); -------------------------------------------------------------------------------- /test/unit/messages/not-supported.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const sinon = require('sinon'); 3 | const Message = require('../../../lib/messages/message'); 4 | const NotSupportedMessage = require('../../../lib/messages/not-supported'); 5 | const Request = require('../../../lib/request'); 6 | 7 | describe('NotSupportedTest', function() { 8 | 9 | const requestStub = sinon.createStubInstance(Request); 10 | let message; 11 | 12 | beforeEach(()=>{ 13 | message = new NotSupportedMessage( requestStub ); 14 | }); 15 | 16 | describe('#constructor()', () => { 17 | 18 | it('should be instance of Message', () => { 19 | expect(message).to.be.instanceOf(Message); 20 | }); 21 | 22 | it('should be create with properties', () => { 23 | expect(message).to.have.property('_request'); 24 | }); 25 | 26 | it('_request property should be instance of Request', () => { 27 | expect(message._request).to.be.instanceOf(Request); 28 | }); 29 | 30 | }); 31 | 32 | describe('#attributes()', function() { 33 | 34 | it('should return all attributes from request all method', () => { 35 | requestStub.all.returns({req:1}); 36 | expect(message.attributes()).to.be.deep.equal({req:1}); 37 | }); 38 | 39 | }); 40 | 41 | describe('#request()', () => { 42 | 43 | it('should return request', () => { 44 | expect(message.request()).to.be.instanceOf(Request); 45 | }); 46 | 47 | }); 48 | 49 | describe('#id()', () => { 50 | 51 | it('should return id from request get method', () => { 52 | requestStub.get.withArgs('id').returns(1); 53 | expect(message.id()).to.be.deep.equal(1); 54 | }); 55 | 56 | }); 57 | 58 | describe('#password()', () => { 59 | 60 | it('should return password from request get method', () => { 61 | requestStub.get.withArgs('password').returns('pass'); 62 | expect(message.password()).to.be.equal('pass'); 63 | }); 64 | 65 | }); 66 | 67 | describe('#ack()', () => { 68 | it('should return id from request get method', () => { 69 | requestStub.get.withArgs('id').returns(1); 70 | expect(message.id()).to.be.deep.equal(1); 71 | }); 72 | }); 73 | 74 | }); -------------------------------------------------------------------------------- /test/unit/messages/receive.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const sinon = require('sinon'); 3 | const Message = require('../../../lib/messages/message'); 4 | const ReceiveMessage = require('../../../lib/messages/receive'); 5 | const Request = require('../../../lib/request'); 6 | 7 | describe('ReceiveTest', function() { 8 | 9 | const requestStub = sinon.createStubInstance(Request); 10 | let message; 11 | 12 | beforeEach(()=>{ 13 | message = new ReceiveMessage( requestStub ); 14 | }); 15 | 16 | describe('#constructor()', () => { 17 | 18 | it('should be instance of Message', () => { 19 | expect(message).to.be.instanceOf(Message); 20 | }); 21 | 22 | it('should be create with properties', () => { 23 | expect(message).to.have.property('_request'); 24 | }); 25 | 26 | it('_request property should be instance of Request', () => { 27 | expect(message._request).to.be.instanceOf(Request); 28 | }); 29 | 30 | }); 31 | 32 | describe('#attributes()', function() { 33 | 34 | it('should return all attributes from request all method', () => { 35 | requestStub.all.returns({req:1}); 36 | expect(message.attributes()).to.be.deep.equal({req:1}); 37 | }); 38 | 39 | }); 40 | 41 | describe('#request()', () => { 42 | 43 | it('should return request', () => { 44 | expect(message.request()).to.be.instanceOf(Request); 45 | }); 46 | 47 | }); 48 | 49 | describe('#id()', () => { 50 | 51 | it('should return id from request get method', () => { 52 | requestStub.get.withArgs('id').returns(1); 53 | expect(message.id()).to.be.deep.equal(1); 54 | }); 55 | 56 | }); 57 | 58 | describe('#password()', () => { 59 | 60 | it('should return password from request get method', () => { 61 | requestStub.get.withArgs('password').returns('pass'); 62 | expect(message.password()).to.be.equal('pass'); 63 | }); 64 | 65 | }); 66 | 67 | describe('#ack()', () => { 68 | it('should return ack message', () => { 69 | requestStub.get.withArgs('receive').returns('123'); 70 | expect(message.ack()).to.be.equal('RECEIVE 123 OK'); 71 | }); 72 | }); 73 | 74 | describe('#receive()', () => { 75 | it('should return receive count', () => { 76 | requestStub.get.withArgs('receive').returns('123'); 77 | expect(message.receive()).to.be.equal('123'); 78 | }); 79 | }); 80 | 81 | describe('#srcnum()', () => { 82 | it('should return phone number', () => { 83 | requestStub.get.withArgs('srcnum').returns('999999999'); 84 | expect(message.srcnum()).to.be.equal('999999999'); 85 | }); 86 | }); 87 | 88 | describe('#msg()', () => { 89 | it('should return text message', () => { 90 | requestStub.get.withArgs('msg').returns('test message'); 91 | expect(message.msg()).to.be.equal('test message'); 92 | }); 93 | }); 94 | 95 | }); -------------------------------------------------------------------------------- /test/unit/messages/record.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const sinon = require('sinon'); 3 | const Message = require('../../../lib/messages/message'); 4 | const RecordMessage = require('../../../lib/messages/record'); 5 | const Request = require('../../../lib/request'); 6 | 7 | describe('RecordTest', function() { 8 | 9 | const requestStub = sinon.createStubInstance(Request); 10 | let message; 11 | 12 | beforeEach(()=>{ 13 | message = new RecordMessage( requestStub ); 14 | }); 15 | 16 | describe('#constructor()', () => { 17 | 18 | it('should be instance of Message', () => { 19 | expect(message).to.be.instanceOf(Message); 20 | }); 21 | 22 | it('should be create with properties', () => { 23 | expect(message).to.have.property('_request'); 24 | }); 25 | 26 | it('_request property should be instance of Request', () => { 27 | expect(message._request).to.be.instanceOf(Request); 28 | }); 29 | 30 | }); 31 | 32 | describe('#attributes()', function() { 33 | 34 | it('should return all attributes from request all method', () => { 35 | requestStub.all.returns({req:1}); 36 | expect(message.attributes()).to.be.deep.equal({req:1}); 37 | }); 38 | 39 | }); 40 | 41 | describe('#request()', () => { 42 | 43 | it('should return request', () => { 44 | expect(message.request()).to.be.instanceOf(Request); 45 | }); 46 | 47 | }); 48 | 49 | describe('#id()', () => { 50 | 51 | it('should return id from request get method', () => { 52 | requestStub.get.withArgs('id').returns(1); 53 | expect(message.id()).to.be.deep.equal(1); 54 | }); 55 | 56 | }); 57 | 58 | describe('#password()', () => { 59 | 60 | it('should return password from request get method', () => { 61 | requestStub.get.withArgs('password').returns('pass'); 62 | expect(message.password()).to.be.equal('pass'); 63 | }); 64 | 65 | }); 66 | 67 | describe('#ack()', () => { 68 | it('should return ack message', () => { 69 | requestStub.get.withArgs('record').returns('123'); 70 | expect(message.ack()).to.be.equal('RECORD 123 OK'); 71 | }); 72 | }); 73 | 74 | describe('#record()', () => { 75 | it('should return record count', () => { 76 | requestStub.get.withArgs('record').returns('123'); 77 | expect(message.record()).to.be.equal('123'); 78 | }); 79 | }); 80 | 81 | describe('#dir()', () => { 82 | it('should return dir', () => { 83 | requestStub.get.withArgs('dir').returns('test dir'); 84 | expect(message.dir()).to.be.equal('test dir'); 85 | }); 86 | }); 87 | 88 | describe('#num()', () => { 89 | it('should return phone number', () => { 90 | requestStub.get.withArgs('num').returns('999999999'); 91 | expect(message.num()).to.be.equal('999999999'); 92 | }); 93 | }); 94 | 95 | }); -------------------------------------------------------------------------------- /test/unit/messages/req.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const sinon = require('sinon'); 3 | const Message = require('../../../lib/messages/message'); 4 | const ReqMessage = require('../../../lib/messages/req'); 5 | const Request = require('../../../lib/request'); 6 | 7 | describe('ReqTest', function() { 8 | 9 | const requestStub = sinon.createStubInstance(Request); 10 | let message; 11 | 12 | beforeEach(()=>{ 13 | message = new ReqMessage( requestStub ); 14 | }); 15 | 16 | describe('#constructor()', () => { 17 | 18 | it('should be instance of Message', () => { 19 | expect(message).to.be.instanceOf(Message); 20 | }); 21 | 22 | it('should be create with properties', () => { 23 | expect(message).to.have.property('_request'); 24 | }); 25 | 26 | it('_request property should be instance of Request', () => { 27 | expect(message._request).to.be.instanceOf(Request); 28 | }); 29 | 30 | }); 31 | 32 | describe('#attributes()', function() { 33 | 34 | it('should return all attributes from request all method', () => { 35 | requestStub.all.returns({req:1}); 36 | expect(message.attributes()).to.be.deep.equal({req:1}); 37 | }); 38 | 39 | }); 40 | 41 | describe('#request()', () => { 42 | 43 | it('should return request', () => { 44 | expect(message.request()).to.be.instanceOf(Request); 45 | }); 46 | 47 | }); 48 | 49 | describe('#id()', () => { 50 | 51 | it('should return id from request get method', () => { 52 | requestStub.get.withArgs('id').returns(1); 53 | expect(message.id()).to.be.deep.equal(1); 54 | }); 55 | 56 | }); 57 | 58 | describe('#password()', () => { 59 | 60 | it('should return password from request get method', () => { 61 | requestStub.get.withArgs('pass').returns('pass'); 62 | expect(message.password()).to.be.equal('pass'); 63 | }); 64 | 65 | }); 66 | 67 | describe('#ack()', () => { 68 | it('should return ack message', () => { 69 | requestStub.get.withArgs('req').returns('123'); 70 | expect(message.ack()).to.be.equal('req:123;status:200;'); 71 | }); 72 | }); 73 | 74 | describe('#req()', () => { 75 | it('should return req count', () => { 76 | requestStub.get.withArgs('req').returns('123'); 77 | expect(message.req()).to.be.equal('123'); 78 | }); 79 | }); 80 | 81 | describe('#num()', () => { 82 | it('should return num', () => { 83 | requestStub.get.withArgs('num').returns('999999999'); 84 | expect(message.num()).to.be.equal('999999999'); 85 | }); 86 | }); 87 | 88 | describe('#signal()', () => { 89 | it('should return signal', () => { 90 | requestStub.get.withArgs('signal').returns('29'); 91 | expect(message.signal()).to.be.equal('29'); 92 | }); 93 | }); 94 | 95 | describe('#gsmStatus()', () => { 96 | it('should return gsmStatus', () => { 97 | requestStub.get.withArgs('gsm_status').returns('test status'); 98 | expect(message.gsmStatus()).to.be.equal('test status'); 99 | }); 100 | }); 101 | 102 | describe('#voipStatus()', () => { 103 | it('should return voipStatus', () => { 104 | requestStub.get.withArgs('voip_status').returns('test status'); 105 | expect(message.voipStatus()).to.be.equal('test status'); 106 | }); 107 | }); 108 | 109 | describe('#voipState()', () => { 110 | it('should return voipState', () => { 111 | requestStub.get.withArgs('voip_state').returns('test state'); 112 | expect(message.voipState()).to.be.equal('test state'); 113 | }); 114 | }); 115 | 116 | describe('#remainTime()', () => { 117 | it('should return remainTime', () => { 118 | requestStub.get.withArgs('remain_time').returns('10'); 119 | expect(message.remainTime()).to.be.equal('10'); 120 | }); 121 | }); 122 | 123 | describe('#imei()', () => { 124 | it('should return imei', () => { 125 | requestStub.get.withArgs('imei').returns('101231241212312'); 126 | expect(message.imei()).to.be.equal('101231241212312'); 127 | }); 128 | }); 129 | 130 | describe('#pro()', () => { 131 | it('should return pro', () => { 132 | requestStub.get.withArgs('pro').returns('test'); 133 | expect(message.pro()).to.be.equal('test'); 134 | }); 135 | }); 136 | 137 | describe('#idle()', () => { 138 | it('should return idle', () => { 139 | requestStub.get.withArgs('idle').returns('1'); 140 | expect(message.idle()).to.be.equal('1'); 141 | }); 142 | }); 143 | 144 | describe('#disableStatus()', () => { 145 | it('should return disableStatus', () => { 146 | requestStub.get.withArgs('disable_status').returns('1'); 147 | expect(message.disableStatus()).to.be.equal('1'); 148 | }); 149 | }); 150 | 151 | describe('#smsLogin()', () => { 152 | it('should return smsLogin', () => { 153 | requestStub.get.withArgs('sms_login').returns('1'); 154 | expect(message.smsLogin()).to.be.equal('1'); 155 | }); 156 | }); 157 | 158 | describe('#smbLogin()', () => { 159 | it('should return smbLogin', () => { 160 | requestStub.get.withArgs('smb_login').returns('1'); 161 | expect(message.smbLogin()).to.be.equal('1'); 162 | }); 163 | }); 164 | 165 | describe('#cellinfo()', () => { 166 | it('should return cellinfo', () => { 167 | requestStub.get.withArgs('cellinfo').returns('1'); 168 | expect(message.cellinfo()).to.be.equal('1'); 169 | }); 170 | }); 171 | 172 | describe('#cgatt()', () => { 173 | it('should return cgatt', () => { 174 | requestStub.get.withArgs('cgatt').returns('1'); 175 | expect(message.cgatt()).to.be.equal('1'); 176 | }); 177 | }); 178 | 179 | }); -------------------------------------------------------------------------------- /test/unit/messages/state.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const sinon = require('sinon'); 3 | const Message = require('../../../lib/messages/message'); 4 | const StateMessage = require('../../../lib/messages/state'); 5 | const Request = require('../../../lib/request'); 6 | 7 | describe('StateTest', function() { 8 | 9 | const requestStub = sinon.createStubInstance(Request); 10 | let message; 11 | 12 | beforeEach(()=>{ 13 | message = new StateMessage( requestStub ); 14 | }); 15 | 16 | describe('#constructor()', () => { 17 | 18 | it('should be instance of Message', () => { 19 | expect(message).to.be.instanceOf(Message); 20 | }); 21 | 22 | it('should be create with properties', () => { 23 | expect(message).to.have.property('_request'); 24 | }); 25 | 26 | it('_request property should be instance of Request', () => { 27 | expect(message._request).to.be.instanceOf(Request); 28 | }); 29 | 30 | }); 31 | 32 | describe('#attributes()', function() { 33 | 34 | it('should return all attributes from request all method', () => { 35 | requestStub.all.returns({req:1}); 36 | expect(message.attributes()).to.be.deep.equal({req:1}); 37 | }); 38 | 39 | }); 40 | 41 | describe('#request()', () => { 42 | 43 | it('should return request', () => { 44 | expect(message.request()).to.be.instanceOf(Request); 45 | }); 46 | 47 | }); 48 | 49 | describe('#id()', () => { 50 | 51 | it('should return id from request get method', () => { 52 | requestStub.get.withArgs('id').returns(1); 53 | expect(message.id()).to.be.deep.equal(1); 54 | }); 55 | 56 | }); 57 | 58 | describe('#password()', () => { 59 | 60 | it('should return password from request get method', () => { 61 | requestStub.get.withArgs('password').returns('pass'); 62 | expect(message.password()).to.be.equal('pass'); 63 | }); 64 | 65 | }); 66 | 67 | describe('#ack()', () => { 68 | it('should return ack message', () => { 69 | requestStub.get.withArgs('state').returns('123'); 70 | expect(message.ack()).to.be.equal('STATE 123 OK'); 71 | }); 72 | }); 73 | 74 | describe('#state()', () => { 75 | it('should return state count', () => { 76 | requestStub.get.withArgs('state').returns('123'); 77 | expect(message.state()).to.be.equal('123'); 78 | }); 79 | }); 80 | 81 | describe('#gsmRemainState()', () => { 82 | it('should return gsm state', () => { 83 | requestStub.get.withArgs('gsm_remain_state').returns('test state'); 84 | expect(message.gsmRemainState()).to.be.equal('test state'); 85 | }); 86 | }); 87 | 88 | 89 | }); -------------------------------------------------------------------------------- /test/unit/request.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const Request = require('../../lib/request'); 3 | const samples = require('../samples'); 4 | 5 | describe('RequestTest', () => { 6 | 7 | const requestClass = new Request( samples.buffers.req, '0.0.0.0', 3000 ); 8 | 9 | describe('#constructor()', () => { 10 | 11 | it('should be created with properties', () => { 12 | 13 | expect(requestClass).to.have.property('_buffer'); 14 | expect(requestClass).to.have.property('_address'); 15 | expect(requestClass).to.have.property('_port'); 16 | expect(requestClass).to.have.property('_attributes'); 17 | 18 | }); 19 | 20 | it('property _port should be an number', () => { 21 | expect(requestClass._port).to.be.an('number'); 22 | }); 23 | 24 | it('property _address should be an buffer', () => { 25 | expect(requestClass._buffer).to.be.an('string'); 26 | }); 27 | 28 | it('property _address should be an string', () => { 29 | expect(requestClass._address).to.be.an('string'); 30 | }); 31 | 32 | it('property _attributes should be an object', () => { 33 | expect(requestClass._attributes).to.be.an('object'); 34 | }); 35 | 36 | }); 37 | 38 | describe('#address()', () => { 39 | 40 | it('should return address', () => { 41 | expect( requestClass.address() ).to.be.equal( '0.0.0.0' ); 42 | }); 43 | 44 | }); 45 | 46 | describe('#port()', () => { 47 | 48 | it('should return port', () => { 49 | expect(requestClass.port() ).to.be.equal(3000); 50 | }); 51 | 52 | }); 53 | 54 | describe('#buffer()', () => { 55 | 56 | it('should return raw buffer', () => { 57 | expect( requestClass.buffer() ).to.be.equal(samples.buffers.req); 58 | }); 59 | 60 | }); 61 | 62 | describe('#_parse()', () => { 63 | 64 | it('should parse from raw buffer to object', () => { 65 | expect( requestClass._parse('key1:val1;key2:val2;key3:val3') ).to.be.deep.equal(requestClass._parse('key1:val1;key2:val2;key3:val3')); 66 | }); 67 | 68 | }); 69 | 70 | describe('#has()', () => { 71 | 72 | it('should return true if key exists', () => { 73 | expect( requestClass.has('req') ).to.be.true; 74 | }); 75 | 76 | it('should return false if key dont exists', () => { 77 | expect( requestClass.has('req2') ).to.be.false; 78 | }); 79 | 80 | }); 81 | 82 | describe('#get()', () => { 83 | 84 | it('should return value if key exist', () => { 85 | expect( requestClass.get('req') ).to.be.equal('27'); 86 | }); 87 | 88 | it('should return null if key dont exists', () => { 89 | expect( requestClass.get('req2') ).to.be.null; 90 | }); 91 | 92 | }); 93 | 94 | describe('#all()', () => { 95 | 96 | const requestClass = new Request( samples.buffers.fake, '0.0.0.0', 3000 ); 97 | 98 | it('should return all attributes', () => { 99 | expect(requestClass.all() ).to.be.deep.equal({ 100 | key1:'val1', 101 | key2:'val2', 102 | key3:'val3' 103 | }); 104 | }); 105 | 106 | }); 107 | 108 | }); -------------------------------------------------------------------------------- /test/unit/server-factory.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const Server = require('../../lib/server'); 3 | const ServerFactory = require('../../lib/server-factory'); 4 | const MessageDispatcher = require('../../lib/message-dispatcher'); 5 | const MessageFactory = require('../../lib/message-factory'); 6 | const sinon = require('sinon'); 7 | 8 | describe('ServerFactoryTest', () => { 9 | 10 | const messageDispatcherStub = sinon.createStubInstance(MessageDispatcher); 11 | const messageFactoryStub = sinon.createStubInstance(MessageFactory); 12 | 13 | describe('#make()', () => { 14 | 15 | it('should be created with port', () => { 16 | const server = ServerFactory.make(9000); 17 | expect(server).to.be.instanceOf(Server); 18 | expect(server._port).to.be.equal(9000); 19 | }); 20 | 21 | it('should be created with port and address', () => { 22 | const server = ServerFactory.make(9000, { 23 | 'address': '192.168.0.2' 24 | }); 25 | 26 | expect(server).to.be.instanceOf(Server); 27 | expect(server._address).to.be.equal('192.168.0.2'); 28 | }); 29 | 30 | it('should be created with port and custom MessageFactory', () => { 31 | const server = ServerFactory.make(9000, { 32 | 'messageFactory': messageFactoryStub 33 | }); 34 | 35 | expect(server).to.be.instanceOf(Server); 36 | expect(server._messageFactory).to.be.equal(messageFactoryStub); 37 | }); 38 | 39 | it('should be created with port and custom MessageDispatcher', () => { 40 | const server = ServerFactory.make(9000, { 41 | 'messageDispatcher': messageDispatcherStub 42 | }); 43 | 44 | expect(server).to.be.instanceOf(Server); 45 | expect(server._messageDispatcher).to.be.equal(messageDispatcherStub); 46 | }); 47 | 48 | 49 | }); 50 | 51 | }); -------------------------------------------------------------------------------- /test/unit/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | const Server = require('../../lib/server'); 5 | const Request = require('../../lib/request'); 6 | const MessageDispatcher = require('../../lib/message-dispatcher'); 7 | const MessageFactory = require('../../lib/message-factory'); 8 | const Socket = require('dgram').Socket; 9 | const Message = require('../../lib/messages/message'); 10 | const sinon = require('sinon'); 11 | const samples = require('../samples'); 12 | 13 | describe('ServerTest', () => { 14 | 15 | const messageDispatcherStub = sinon.createStubInstance(MessageDispatcher); 16 | const messageFactoryStub = sinon.createStubInstance(MessageFactory); 17 | let server; 18 | 19 | beforeEach(()=>{ 20 | server = new Server({ 21 | 'port': 9000, 22 | 'address': '0.0.0.0', 23 | 'messageDispatcher': messageDispatcherStub, 24 | 'messageFactory': messageFactoryStub 25 | }); 26 | }); 27 | 28 | describe('#constructor()', () => { 29 | 30 | it('should be created with properties', () => { 31 | 32 | expect(server).to.have.property('_port'); 33 | expect(server).to.have.property('_address'); 34 | expect(server).to.have.property('_messageDispatcher'); 35 | expect(server).to.have.property('_messageFactory'); 36 | expect(server).to.have.property('_socket'); 37 | }); 38 | 39 | it('property _port should be number', () => { 40 | expect(server._port).to.be.an('number'); 41 | }); 42 | 43 | it('property _address should be string', () => { 44 | expect(server._address).to.be.an('string'); 45 | }); 46 | 47 | it('property _messageDispatcher should be instance of MessageDispatcher', () => { 48 | expect(server._messageDispatcher).to.be.instanceOf(MessageDispatcher); 49 | }); 50 | 51 | it('property _messageFactory should be instance of MessageFactory', () => { 52 | expect(server._messageFactory).to.be.instanceOf(MessageFactory); 53 | }); 54 | 55 | it('property _socket should be instance of Socket', () => { 56 | expect(server._socket).to.be.instanceOf(Socket); 57 | }); 58 | 59 | }); 60 | 61 | describe('#_makeGoipRequest()', () => { 62 | it('should create request instance', () => { 63 | expect(server._makeGoipRequest( samples.buffers.req, { 64 | 'port': 9000, 65 | 'address': '0.0.0.0' 66 | })).to.be.instanceOf(Request); 67 | }); 68 | }); 69 | 70 | describe('#_messageHandler()', () => { 71 | 72 | const server = new Server({ 73 | 'port': 9000, 74 | 'address': '0.0.0.0', 75 | 'messageDispatcher': messageDispatcherStub, 76 | 'messageFactory': messageFactoryStub 77 | }); 78 | 79 | const messageStub = sinon.createStubInstance(Message); 80 | 81 | messageFactoryStub.make.returns( messageStub ); 82 | messageStub.ack.returns('ack message'); 83 | 84 | const spy = sinon.spy(server, "_send"); 85 | 86 | server._messageHandler( samples.buffers.req, { 87 | 'port': 9000, 88 | 'address': '0.0.0.0' 89 | }); 90 | 91 | server.close(); 92 | 93 | it('should make message from MessageFactory', () => { 94 | expect( messageFactoryStub.make.calledOnce ).to.be.true; 95 | }); 96 | 97 | it('should send ack message from Message class', () => { 98 | expect( spy.withArgs('ack message', '0.0.0.0', 9000).calledOnce ).to.be.true; 99 | }); 100 | 101 | it('should dispatch message by MessageDispatcher', () => { 102 | expect( messageDispatcherStub.dispatch.withArgs('message', messageStub).calledTwice ).to.be.true; 103 | }); 104 | 105 | }); 106 | 107 | describe('#_errorHandler()', () => { 108 | 109 | it('should close socket connection', () => { 110 | server.run(); 111 | const spyServer = sinon.spy(server, "close"); 112 | const spySocket = sinon.spy(server._socket, "close"); 113 | server._errorHandler('error message'); 114 | expect( spyServer.calledOnce ).to.be.true; 115 | expect( spySocket.calledOnce ).to.be.true; 116 | }); 117 | 118 | }); 119 | 120 | describe('#close()', () => { 121 | 122 | it('should close socket connection', () => { 123 | server.run(); 124 | const spySocket = sinon.spy(server._socket, "close"); 125 | server.close(); 126 | expect( spySocket.calledOnce ).to.be.true; 127 | }); 128 | 129 | }); 130 | 131 | describe('#onMessage()', () => { 132 | 133 | it('should register message linstener in MessageDispatcher', () => { 134 | 135 | const callback = sinon.spy(); 136 | server.onMessage('delivery', callback); 137 | expect( messageDispatcherStub.listen.withArgs('delivery',callback).calledOnce ).to.be.true; 138 | 139 | }); 140 | 141 | }); 142 | 143 | describe('#onAll()', () => { 144 | 145 | it('should register message linstener for all messages', () => { 146 | 147 | const callback = sinon.spy(); 148 | 149 | const spyOnMessage = sinon.spy(server, 'onMessage'); 150 | 151 | server.onAll(callback); 152 | 153 | expect( spyOnMessage.withArgs('message',callback).calledOnce ).to.be.true; 154 | expect( messageDispatcherStub.listen.withArgs('message',callback).calledOnce ).to.be.true; 155 | }); 156 | 157 | }); 158 | 159 | describe('#onRequest()', () => { 160 | 161 | it('should register message linstener for request messages', () => { 162 | 163 | const callback = sinon.spy(); 164 | 165 | const spyOnMessage = sinon.spy(server, 'onMessage'); 166 | 167 | server.onRequest(callback); 168 | 169 | expect( spyOnMessage.withArgs('request',callback).calledOnce ).to.be.true; 170 | expect( messageDispatcherStub.listen.withArgs('request',callback).calledOnce ).to.be.true; 171 | }); 172 | 173 | }); 174 | 175 | describe('#onNotSupported()', () => { 176 | 177 | it('should register message linstener for not supported messages', () => { 178 | 179 | const callback = sinon.spy(); 180 | 181 | const spyOnMessage = sinon.spy(server, 'onMessage'); 182 | 183 | server.onNotSupported(callback); 184 | 185 | expect( spyOnMessage.withArgs('notsupported',callback).calledOnce ).to.be.true; 186 | expect( messageDispatcherStub.listen.withArgs('notsupported',callback).calledOnce ).to.be.true; 187 | }); 188 | 189 | }); 190 | 191 | describe('#onReceive()', () => { 192 | 193 | it('should register message linstener for receive messages', () => { 194 | 195 | const callback = sinon.spy(); 196 | 197 | const spyOnMessage = sinon.spy(server, 'onMessage'); 198 | 199 | server.onReceive(callback); 200 | 201 | expect( spyOnMessage.withArgs('receive',callback).calledOnce ).to.be.true;; 202 | expect( messageDispatcherStub.listen.withArgs('receive',callback).calledOnce ).to.be.true; 203 | }); 204 | 205 | }); 206 | 207 | describe('#onDeliver()', () => { 208 | 209 | it('should register message linstener for deliver messages', () => { 210 | 211 | const callback = sinon.spy(); 212 | 213 | const spyOnMessage = sinon.spy(server, 'onMessage'); 214 | 215 | server.onDeliver(callback); 216 | 217 | expect( spyOnMessage.withArgs('deliver',callback).calledOnce ).to.be.true; 218 | expect( messageDispatcherStub.listen.withArgs('deliver',callback).calledOnce ).to.be.true; 219 | }); 220 | 221 | }); 222 | 223 | describe('#onHangup()', () => { 224 | 225 | it('should register message linstener for hangup messages', () => { 226 | 227 | const callback = sinon.spy(); 228 | 229 | const spyOnMessage = sinon.spy(server, 'onMessage'); 230 | 231 | server.onHangup(callback); 232 | 233 | expect( spyOnMessage.withArgs('hangup',callback).calledOnce ).to.be.true; 234 | expect( messageDispatcherStub.listen.withArgs('hangup',callback).calledOnce ).to.be.true; 235 | }); 236 | 237 | }); 238 | 239 | describe('#onRecord()', () => { 240 | 241 | it('should register message linstener for record messages', () => { 242 | 243 | const callback = sinon.spy(); 244 | 245 | const spyOnMessage = sinon.spy(server, 'onMessage'); 246 | 247 | server.onRecord(callback); 248 | 249 | expect( spyOnMessage.withArgs('record',callback).calledOnce ).to.be.true; 250 | expect( messageDispatcherStub.listen.withArgs('record',callback).calledOnce ).to.be.true; 251 | }); 252 | 253 | }); 254 | 255 | describe('#onState()', () => { 256 | 257 | it('should register message linstener for state messages', () => { 258 | 259 | const callback = sinon.spy(); 260 | 261 | const spyOnMessage = sinon.spy(server, 'onMessage'); 262 | 263 | server.onState(callback); 264 | 265 | expect( spyOnMessage.withArgs('state',callback).calledOnce ).to.be.true; 266 | expect( messageDispatcherStub.listen.withArgs('state',callback).calledOnce ).to.be.true; 267 | 268 | }); 269 | 270 | }); 271 | 272 | describe('#onServerError()', () => { 273 | 274 | it('should register linstener for server error event', () => { 275 | 276 | const spySocketOn = sinon.spy(server._socket, 'on'); 277 | const callback = sinon.spy(); 278 | 279 | server.onServerError(callback); 280 | 281 | expect( spySocketOn.withArgs('error',callback).calledOnce ).to.be.true; 282 | 283 | }); 284 | 285 | }); 286 | 287 | describe('#onServerListening()', () => { 288 | 289 | it('should register linstener for server listening event', () => { 290 | 291 | const spySocketOn = sinon.spy(server._socket, 'on'); 292 | const callback = sinon.spy(); 293 | 294 | server.onServerListening(callback); 295 | 296 | expect( spySocketOn.withArgs('listening',callback).calledOnce ).to.be.true; 297 | 298 | }); 299 | 300 | }); 301 | 302 | }); -------------------------------------------------------------------------------- /test/unit/sms/http-sms.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const HttpSms = require('../../../lib/sms/http-sms') 3 | const sinon = require('sinon'); 4 | const nock = require('nock'); 5 | const samples = require('../../samples'); 6 | 7 | describe('HttpSmsTest', () => { 8 | 9 | const sendPath = nock('http://192.168.0.12') 10 | .get('/default/en_US/send.html') 11 | .query({ 12 | u:'admin', 13 | p:'admin', 14 | l:1, 15 | n:'999999999', 16 | m:'test message' 17 | }) 18 | 19 | const statusPath = nock('http://192.168.0.12') 20 | .get('/default/en_US/send_status.xml') 21 | .query({ 22 | u:'admin', 23 | p:'admin', 24 | }) 25 | 26 | const httpSms = new HttpSms('http://192.168.0.12', 1, 'admin', 'admin'); 27 | 28 | describe('#constructor()', () => { 29 | 30 | it('should be create with properties', () => { 31 | expect(httpSms).to.have.property('_url'); 32 | expect(httpSms).to.have.property('_line'); 33 | expect(httpSms).to.have.property('_login'); 34 | expect(httpSms).to.have.property('_password'); 35 | expect(httpSms).to.have.property('_options'); 36 | }); 37 | 38 | it('_options property should have default values', () => { 39 | expect(httpSms._options).to.be.deep.equal({ 40 | 'sendDir': '/default/en_US/send.html', 41 | 'statusDir': '/default/en_US/send_status.xml', 42 | 'waitForStatus': false, 43 | 'waitTries': 10, 44 | 'waitTime': 1000, 45 | }); 46 | }); 47 | 48 | it('_options property can be overridden', () => { 49 | 50 | const httpSms = new HttpSms('http://192.168.0.11', 1, 'admin', 'admin', { 51 | 'sendDir': 'testSendDir', 52 | 'statusDir': 'testStatusDir', 53 | 'waitForStatus': true, 54 | 'waitTries': 1, 55 | 'waitTime': 1, 56 | }); 57 | 58 | expect(httpSms._options).to.be.deep.equal({ 59 | 'sendDir': 'testSendDir', 60 | 'statusDir': 'testStatusDir', 61 | 'waitForStatus': true, 62 | 'waitTries': 1, 63 | 'waitTime': 1, 64 | }); 65 | }); 66 | 67 | }); 68 | 69 | describe('#_prepareUrl()', () => { 70 | it('should remove last slash from url', () => { 71 | expect(httpSms._prepareUrl('http://192.168.0.11/')).to.be.equal('http://192.168.0.11'); 72 | }); 73 | }); 74 | 75 | describe('#_parse()', () => { 76 | 77 | it('should throw error when response have error', () => { 78 | expect(() => httpSms._parse('ERROR,user or password error')).to.throw(Error,'ERROR,user or password error'); 79 | }); 80 | 81 | it('should throw error when response dont have id', () => { 82 | expect(() => httpSms._parse('Sending,L1 Send SMS to:999999999;')).to.throw(Error,'Sms id not found in respons'); 83 | }); 84 | 85 | it('should return object response with id, raw response and status', () => { 86 | expect(httpSms._parse('Sending,L1 Send SMS to:999999999; ID:0000006d')).to.be.deep.equal({ 87 | 'id': '0000006d', 88 | 'raw': 'Sending,L1 Send SMS to:999999999; ID:0000006d', 89 | 'status': 'sending' 90 | }); 91 | }); 92 | 93 | }); 94 | 95 | describe('#isSend()', () => { 96 | 97 | it('should throw error when status list not found', async () => { 98 | 99 | statusPath.reply(200, ''); 100 | 101 | let err = null; 102 | try { 103 | await httpSms.isSend('0000006d'); 104 | } catch (error) { 105 | err = error; 106 | } 107 | 108 | expect(err).to.be.instanceOf(Error); 109 | expect(err.message).to.be.equal('Sms send status not found!'); 110 | 111 | }); 112 | 113 | it('should throw error when status have error', async () => { 114 | statusPath.reply(200, samples.xmlError); 115 | 116 | let err = null; 117 | try { 118 | await httpSms.isSend('0000006d'); 119 | } catch (error) { 120 | err = error; 121 | } 122 | 123 | expect(err).to.be.instanceOf(Error); 124 | expect(err.message).to.be.equal('error message'); 125 | }); 126 | 127 | it('should return false if status is different than done', async () => { 128 | statusPath.reply(200, samples.xmlStartingStatus); 129 | 130 | const response = await httpSms.isSend('0000006d'); 131 | 132 | expect(response).to.be.false; 133 | }); 134 | 135 | it('should return false id not found', async () => { 136 | statusPath.reply(200, samples.xmlDone); 137 | 138 | const response = await httpSms.isSend('0000006d2'); 139 | 140 | expect(response).to.be.false; 141 | }); 142 | 143 | it('should return true if status is done', async () => { 144 | statusPath.reply(200, samples.xmlDone); 145 | 146 | const response = await httpSms.isSend('0000006d'); 147 | 148 | expect(response).to.be.true; 149 | }); 150 | 151 | }); 152 | 153 | describe('#_waitForStatus()', () => { 154 | 155 | it('should call isSend with id', async () => { 156 | 157 | statusPath.reply(200, samples.xmlDone); 158 | 159 | const httpSms = new HttpSms('http://192.168.0.12',1,'admin','admin', { 160 | 'waitForStatus': true, 161 | 'waitTries': 1, 162 | 'waitTime': 1 163 | }); 164 | 165 | const spyIsSend = sinon.spy(httpSms,'isSend'); 166 | 167 | await httpSms._waitForStatus('0000006d') 168 | 169 | expect( spyIsSend.withArgs('0000006d').calledOnce ).to.be.true; 170 | }); 171 | 172 | }); 173 | 174 | describe('#send()', () => { 175 | 176 | 177 | it('should throw Error when response have error message', async () => { 178 | sendPath.reply(200, 'ERROR,user or password error'); 179 | 180 | let err = null; 181 | try { 182 | const res = await httpSms.send('999999999', 'test message'); 183 | } catch (error) { 184 | err = error; 185 | } 186 | 187 | expect(err).to.be.instanceOf(Error); 188 | expect(err.message).to.be.equal('ERROR,user or password error'); 189 | }); 190 | 191 | it('should return object response with id, raw response and status', async () => { 192 | 193 | sendPath.reply(200, 'Sending,L1 Send SMS to:999999999; ID:0000006d'); 194 | 195 | const res = await httpSms.send('999999999', 'test message'); 196 | 197 | expect(res).to.be.deep.equal({ 198 | 'id': '0000006d', 199 | 'raw': 'Sending,L1 Send SMS to:999999999; ID:0000006d', 200 | 'status': 'sending' 201 | }); 202 | 203 | }); 204 | 205 | it('should return object response with id, raw response and status send when waitForStatus is set to true if sms was sent', async () => { 206 | 207 | sendPath.reply(200, 'Sending,L1 Send SMS to:999999999; ID:0000006d'); 208 | statusPath.reply(200, samples.xmlDone); 209 | 210 | const httpSms = new HttpSms('http://192.168.0.12',1,'admin','admin', { 211 | 'waitForStatus': true, 212 | 'waitTries': 1, 213 | 'waitTime': 1 214 | }); 215 | 216 | const res = await httpSms.send('999999999', 'test message'); 217 | 218 | expect(res).to.be.deep.equal({ 219 | 'id': '0000006d', 220 | 'raw': 'Sending,L1 Send SMS to:999999999; ID:0000006d', 221 | 'status': 'send' 222 | }); 223 | 224 | }); 225 | 226 | it('should return object response with id, raw response, status error and error message when waitForStatus is set to true if sms was not sent', async () => { 227 | 228 | sendPath.reply(200, 'Sending,L1 Send SMS to:999999999; ID:0000006d'); 229 | statusPath.reply(200, samples.xmlError); 230 | 231 | const httpSms = new HttpSms('http://192.168.0.12',1,'admin','admin', { 232 | 'waitForStatus': true, 233 | 'waitTries': 1, 234 | 'waitTime': 1 235 | }); 236 | 237 | const res = await httpSms.send('999999999', 'test message'); 238 | 239 | expect(res).to.be.deep.equal({ 240 | 'id': '0000006d', 241 | 'raw': 'Sending,L1 Send SMS to:999999999; ID:0000006d', 242 | 'status': 'error', 243 | 'error': 'error message' 244 | }); 245 | 246 | }); 247 | 248 | it('should return object response with id, raw response and status sending when waitForStatus is set to true if sms was not sent', async () => { 249 | 250 | sendPath.reply(200, 'Sending,L1 Send SMS to:999999999; ID:0000006d'); 251 | statusPath.reply(200, samples.xmlStartingStatus); 252 | 253 | const httpSms = new HttpSms('http://192.168.0.12',1,'admin','admin', { 254 | 'waitForStatus': true, 255 | 'waitTries': 1, 256 | 'waitTime': 1 257 | }); 258 | 259 | const res = await httpSms.send('999999999', 'test message'); 260 | 261 | expect(res).to.be.deep.equal({ 262 | 'id': '0000006d', 263 | 'raw': 'Sending,L1 Send SMS to:999999999; ID:0000006d', 264 | 'status': 'sending' 265 | }); 266 | 267 | }); 268 | 269 | it('should call isSend() 10 times when waitTries i set to 10', async () => { 270 | 271 | sendPath.reply(200, 'Sending,L1 Send SMS to:999999999; ID:0000006d'); 272 | statusPath.times(10).reply(200, samples.xmlStartingStatus); 273 | 274 | const httpSms = new HttpSms('http://192.168.0.12',1,'admin','admin', { 275 | 'waitForStatus': true, 276 | 'waitTries': 10, 277 | 'waitTime': 1 278 | }); 279 | 280 | const spyIsSend = sinon.spy(httpSms,'isSend'); 281 | 282 | const res = await httpSms.send('999999999', 'test message'); 283 | 284 | expect( spyIsSend.callCount ).to.be.equal(10); 285 | 286 | expect(res).to.be.deep.equal({ 287 | 'id': '0000006d', 288 | 'raw': 'Sending,L1 Send SMS to:999999999; ID:0000006d', 289 | 'status': 'sending' 290 | }); 291 | 292 | }); 293 | 294 | 295 | }); 296 | 297 | }); -------------------------------------------------------------------------------- /test/unit/sms/socket-sms.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const sinon = require('sinon'); 3 | const nock = require('nock'); 4 | const samples = require('../../samples'); 5 | const Socket = require('dgram').Socket; 6 | const FakeSocketServer = require('../fake-socket-server'); 7 | const proxyquire = require('proxyquire').noCallThru(); 8 | const dgram = require('dgram'); 9 | const SocketSms = proxyquire('../../../lib/sms/socket-sms', { 10 | 'dgram': { 11 | 'createSocket': (type) => { 12 | return new FakeSocketServer(type); 13 | } 14 | } 15 | }); 16 | 17 | describe('SocketSmsTest', () => { 18 | 19 | let socketSms; 20 | 21 | beforeEach(()=>{ 22 | socketSms = new SocketSms('192.168.0.11', 9991, 'password', 10); 23 | }); 24 | 25 | describe('#constructor()', () => { 26 | 27 | it('should be create with properties', () => { 28 | expect(socketSms).to.have.property('_address'); 29 | expect(socketSms).to.have.property('_port'); 30 | expect(socketSms).to.have.property('_id'); 31 | expect(socketSms).to.have.property('_password'); 32 | expect(socketSms).to.have.property('_socket'); 33 | expect(socketSms).to.have.property('_pendingRequest'); 34 | expect(socketSms).to.have.property('_expectedResponse'); 35 | expect(socketSms).to.have.property('_timeoutHandler'); 36 | expect(socketSms).to.have.property('_timeout'); 37 | expect(socketSms).to.have.property('_requestStatus'); 38 | }); 39 | 40 | it('_address property should be string', () => { 41 | expect(socketSms._address).to.be.string; 42 | }); 43 | 44 | it('_port property should be number', () => { 45 | expect(socketSms._port).to.be.an('number'); 46 | }); 47 | 48 | it('_port property should be null', () => { 49 | expect(socketSms._id).to.be.null; 50 | }); 51 | 52 | it('_password property should be string', () => { 53 | expect(socketSms._password).to.be.an('string'); 54 | }); 55 | 56 | it('_socket property should be instance of Socket', () => { 57 | expect(socketSms._socket).to.be.instanceOf(Socket); 58 | }); 59 | 60 | it('_pendingRequest property should be null', () => { 61 | expect(socketSms._pendingRequest).to.be.null; 62 | }); 63 | 64 | it('_expectedResponse property should be null', () => { 65 | expect(socketSms._expectedResponse).to.be.null; 66 | }); 67 | 68 | it('_timeoutHandler property should be null', () => { 69 | expect(socketSms._timeoutHandler).to.be.null; 70 | }); 71 | 72 | it('_timeout property should be 10', () => { 73 | expect(socketSms._timeout).to.be.equal(10); 74 | }); 75 | 76 | it('_requestStatus property should be object with error and success flags', () => { 77 | expect(socketSms._requestStatus).to.be.deep.equal({ 78 | error: 0, 79 | success: 2, 80 | }); 81 | }); 82 | }); 83 | 84 | describe('#_isTimeout()', () => { 85 | it('should reject promise with error after timeout and close socket connection', async () => { 86 | 87 | const spyClose = sinon.spy(socketSms,'close'); 88 | 89 | let err; 90 | try { 91 | await new Promise((resolve, reject)=> { 92 | socketSms._isTimeout(reject); 93 | }); 94 | } catch (error) { 95 | err = error; 96 | } 97 | 98 | expect(err).to.be.instanceOf(Error); 99 | expect(err.message).to.be.equal('Timeout in null'); 100 | expect(spyClose.calledOnce).to.be.true; 101 | 102 | }); 103 | }); 104 | 105 | describe('#_checkResponse()', () => { 106 | 107 | it('should return true if response is expected', () => { 108 | const msg = 'DONE 1\n'; 109 | const expected = 'DONE'; 110 | const id = 1; 111 | expect( socketSms._checkResponse(msg,expected,id) ).to.be.true 112 | }); 113 | 114 | it('should return false if response is not expected', () => { 115 | const msg = 'PASSWORD 1\n'; 116 | const expected = 'DONE'; 117 | const id = 1; 118 | expect( socketSms._checkResponse(msg,expected,id) ).to.be.false 119 | }); 120 | 121 | }); 122 | 123 | describe('#close()', () => { 124 | it('should close socket connection', () => { 125 | const spyClose = sinon.spy(socketSms,'close'); 126 | const spyCloseSocket = sinon.spy(socketSms._socket,'close'); 127 | 128 | socketSms.close(); 129 | 130 | expect(spyClose.calledOnce).to.be.true; 131 | expect(spyCloseSocket.calledOnce).to.be.true; 132 | }); 133 | }); 134 | 135 | describe('#_sendRequest()', () => { 136 | 137 | it('should return message if server reponse as expected', async () => { 138 | const message = await socketSms._sendRequest('test message', 'test-message', 'test-request'); 139 | expect(message).to.be.equal('test message ok'); 140 | }); 141 | 142 | it('should throw error if server reponse with error', async () => { 143 | let err; 144 | try { 145 | await socketSms._sendRequest('test error', 'test-error', 'test-request'); 146 | } catch (error) { 147 | err = error; 148 | } 149 | 150 | expect(err).to.be.instanceOf(Error); 151 | expect(err.message).to.be.equal('test error'); 152 | }); 153 | 154 | it('should throw timeout error if server not response as expected', async () => { 155 | let err; 156 | try { 157 | await socketSms._sendRequest('test timeout', 'test-timeout', 'test-timeout'); 158 | } catch (error) { 159 | err = error; 160 | } 161 | 162 | expect(err).to.be.instanceOf(Error); 163 | expect(err.message).to.be.equal('Timeout in test-timeout'); 164 | }); 165 | }); 166 | 167 | describe('#_sendBulkSmsRequest()', () => { 168 | 169 | it('should send command with message and wait for response', async () => { 170 | const spySendRequest = sinon.spy(socketSms, '_sendRequest'); 171 | const response = await socketSms._sendBulkSmsRequest('sms message', 123); 172 | expect(response).to.be.equal('PASSWORD 123'); 173 | expect(spySendRequest.withArgs('MSG 123 11 sms message', 'PASSWORD', 'sendBulkSmsRequest').calledOnce).to.be.true; 174 | }); 175 | 176 | }); 177 | 178 | describe('#_sendAuthRequest()', () => { 179 | 180 | it('should send command with message and wait for response', async () => { 181 | const spySendRequest = sinon.spy(socketSms, '_sendRequest'); 182 | const response = await socketSms._sendAuthRequest('pass',123); 183 | expect(response).to.be.equal('SEND 123'); 184 | expect(spySendRequest.withArgs('PASSWORD 123 pass', 'SEND', 'sendAuthRequest').calledOnce).to.be.true; 185 | }); 186 | 187 | }); 188 | 189 | describe('#_sendNumberRequest()', () => { 190 | 191 | it('should send command with message and wait for response', async () => { 192 | const spySendRequest = sinon.spy(socketSms, '_sendRequest'); 193 | const response = await socketSms._sendNumberRequest(999999999, 123); 194 | expect(response).to.be.equal('OK 123 1 11'); 195 | expect(spySendRequest.withArgs('SEND 123 1 999999999', 'OK', 'sendNumberRequest').calledOnce).to.be.true; 196 | }); 197 | 198 | }); 199 | 200 | describe('#_sendEndRequest()', () => { 201 | 202 | it('should send command with message and wait for response', async () => { 203 | const spySendRequest = sinon.spy(socketSms, '_sendRequest'); 204 | const response = await socketSms._sendEndRequest(123); 205 | expect(response).to.be.equal('DONE 123'); 206 | expect(spySendRequest.withArgs('DONE 123', 'DONE', 'sendEndRequest').calledOnce).to.be.true; 207 | }); 208 | 209 | }); 210 | 211 | describe('#send()', () => { 212 | 213 | it('should return object response when send with id', async () => { 214 | const spySendRequest = sinon.spy(socketSms, '_sendRequest'); 215 | const response = await socketSms.send('999999999', 'test', 123); 216 | 217 | expect(response).to.be.deep.equal({ 218 | 'sendid': '123', 219 | 'telid': '1', 220 | 'sms_no': '11', 221 | 'raw': 'OK 123 1 11' 222 | }); 223 | 224 | expect(spySendRequest.callCount).to.be.equal(4); 225 | }); 226 | 227 | it('should return object response when send without id', async () => { 228 | const spySendRequest = sinon.spy(socketSms, '_sendRequest'); 229 | const response = await socketSms.send('999999999', 'test'); 230 | 231 | expect(response).to.be.deep.equal({ 232 | 'sendid': socketSms._id.toString(), 233 | 'telid': '1', 234 | 'sms_no': '11', 235 | 'raw': 'OK ' + socketSms._id + ' 1 11' 236 | }); 237 | 238 | expect(spySendRequest.callCount).to.be.equal(4); 239 | }); 240 | 241 | it('should send multiple times with the same connection', async () => { 242 | const spySendRequest = sinon.spy(socketSms, '_sendRequest'); 243 | const response1 = await socketSms.send('999999999', 'test'); 244 | 245 | expect(response1).to.be.deep.equal({ 246 | 'sendid': socketSms._id.toString(), 247 | 'telid': '1', 248 | 'sms_no': '11', 249 | 'raw': 'OK ' + socketSms._id + ' 1 11' 250 | }); 251 | 252 | const response2 = await socketSms.send('999999999', 'test'); 253 | 254 | expect(response2).to.be.deep.equal({ 255 | 'sendid': socketSms._id.toString(), 256 | 'telid': '1', 257 | 'sms_no': '11', 258 | 'raw': 'OK ' + socketSms._id + ' 1 11' 259 | }); 260 | 261 | expect(spySendRequest.callCount).to.be.equal(8); 262 | }); 263 | }); 264 | 265 | describe('#sendOne()', () => { 266 | 267 | it('should return object response and close socket when send with id', async () => { 268 | const spySendRequest = sinon.spy(socketSms, '_sendRequest'); 269 | const spyClose = sinon.spy(socketSms,'close'); 270 | 271 | const response = await socketSms.sendOne('999999999', 'test', 123); 272 | 273 | expect(response).to.be.deep.equal({ 274 | 'sendid': '123', 275 | 'telid': '1', 276 | 'sms_no': '11', 277 | 'raw': 'OK 123 1 11' 278 | }); 279 | 280 | expect(spyClose.calledOnce).to.be.true; 281 | expect(spySendRequest.callCount).to.be.equal(4); 282 | }); 283 | 284 | it('should return object response and close socket when send without id', async () => { 285 | const spySendRequest = sinon.spy(socketSms, '_sendRequest'); 286 | const response = await socketSms.sendOne('999999999', 'test'); 287 | 288 | expect(response).to.be.deep.equal({ 289 | 'sendid': socketSms._id.toString(), 290 | 'telid': '1', 291 | 'sms_no': '11', 292 | 'raw': 'OK ' + socketSms._id + ' 1 11' 293 | }); 294 | 295 | expect(spySendRequest.callCount).to.be.equal(4); 296 | }); 297 | }); 298 | 299 | }); -------------------------------------------------------------------------------- /test/unit/util.js: -------------------------------------------------------------------------------- 1 | const assert = require('chai').assert; 2 | const util = require('../../lib/util'); 3 | 4 | describe('utilTest', () => { 5 | 6 | describe('#randomInt()', () => { 7 | 8 | it('should return random number between min max', () => { 9 | let number = util.randomInt(1,10); 10 | assert.isNumber( number ); 11 | assert.isTrue( number >= 1 && number <= 10 ); 12 | 13 | number = util.randomInt(10000,99999); 14 | 15 | assert.isNumber( number ); 16 | assert.isTrue( number >= 10000 && number <= 99999 ); 17 | }); 18 | 19 | }); 20 | 21 | }); --------------------------------------------------------------------------------