@bitfinex/bfx-svc-test-helper 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # bfx-svc-test-helper
2
+
3
+ Test helper to help with common day to day testing tasks.
4
+
5
+ ## Do you see yourself copy/pasting or doing the same setup code again?
6
+
7
+ Maybe it's time for a pull request or an issue! `bfx-svc-test-helper` is open
8
+ for improvements and suggestions that make our day to day life as a team easier!
9
+
10
+ ## Usage in tests
11
+
12
+ The API supports Promise and callback based APIs. For a common example,
13
+ see [example.js](example.js).
14
+
15
+ You can also stub/mock Grenache services. See
16
+ [example-fauxgrenache.js](example-fauxgrenache.js) for an example.
17
+
18
+ ## API
19
+
20
+ ### `createGrapes([opts]) => Grapes`
21
+
22
+ Creates an instance to manage Grapes for testing.
23
+
24
+ ### Arguments
25
+
26
+ - `opts` is an object of additional options, there is able to pass:
27
+ - `ports` is an array of object, for example:
28
+
29
+ ```js
30
+ [{
31
+ dht_port: 20002,
32
+ dht_bootstrap: ['127.0.0.1:20001'],
33
+ api_port: 40001
34
+ }, {
35
+ dht_port: 20001,
36
+ dht_bootstrap: ['127.0.0.1:20002'],
37
+ api_port: 30001
38
+ }]
39
+ ```
40
+
41
+ ### `grapes.start([cb]) => Promise`
42
+
43
+ Creates two "default Grapes" and calls the callback when the found each other.
44
+
45
+ ### `grapes.onAnnounce([servicename], [cb]) => Promise`
46
+
47
+ Calls the callback as soon as the service is announced on the network.
48
+ Leave blank to match any announce.
49
+
50
+ ### `grapes.stop([cb]) => Promise`
51
+
52
+ Stops the Grapes.
53
+
54
+ ### `createWorker(worker, [grapes]) => Worker`
55
+
56
+ Creates an svc-js worker instance.
57
+
58
+ ### `worker.start([cb]) => Promise`
59
+
60
+ Starts the worker. If `grapes` were passed via `createWorker`, resolves as
61
+ soon as the service name is announced on the network.
62
+
63
+ ### `worker.stop([cb]) => Promise`
64
+
65
+ Stops the worker.
66
+
67
+ ### `createClient(worker) => Client`
68
+
69
+ Sets up a typical PeerClient needed for most use cases.
70
+ `worker` can be the name of the worker e.g. `rest:util:net`, or can be a
71
+ `<Worker>` instance from `createWorker`
72
+
73
+ ### `client.request(args, [opts], [cb]) => Promise`
74
+
75
+ Makes a request to the configured worker.
76
+
77
+ ### `client.stop([cb]) => Promise`
78
+
79
+ Stops the client.
80
+
81
+ ### `createFxGrenache(mocks, grapes|url, [opts]) => FauxGrenacheServer`
82
+
83
+ Spins up Grenache service mocks.
84
+
85
+ `opts.port` - the port the server will listen to, defaults to `1557`
86
+ `opts.link` - custom / own link, can be used for customised setups
87
+
88
+ Second parameter is either a `Grapes` instance or a http url pointing
89
+ to the Grenache Grape HTTP Server.
90
+
91
+ ### `fxgrenache.start([cb]) => Promise`
92
+
93
+ Makes a request to the configured mockserver.
94
+
95
+ ### `fxgrenache.stop([cb]) => Promise`
96
+
97
+ Stops the server.
98
+
99
+ ### `createServer(mocks, opts) => Server`
100
+
101
+ Sets up a HTTP stub/mock server.
102
+
103
+ `opts.port` - the port the server will listen to.
104
+ `opts.debug` - print the data sent from the client via post
105
+
106
+ `opts.debug` is useful when you are creating new mock definitions, especially
107
+ if you use an external client library to send the request.
108
+
109
+ `mocks` can be a mock definition or an array of mock definitions.
110
+
111
+ The server can basically match, test and return anything on request/response,
112
+ sent payloads etc. For convenience, there are shorthands that should suffice
113
+ in most cases: [example-server-simple.js](example-server-simple.js).
114
+
115
+ For advanced route matching and testing of incoming requests, see [example-server-advanced.js](example-server-advanced.js)
116
+
117
+ ### `server.start([cb]) => Promise`
118
+
119
+ Makes a request to the configured mockserver.
120
+
121
+ ### `server.stop([cb]) => Promise`
122
+
123
+ Stops the server.
124
+
125
+ ### `utils.getApi(worker) => Api`
126
+
127
+ Returns the loc.api instance of the worker
128
+
129
+ ### `utils.getGrapeApiPort(grapesWorker) => port`
130
+
131
+ Gets the HTTP port of one of the Grape workers
132
+
133
+ Example:
134
+
135
+ ```js
136
+ const createGrapes = require('bfx-svc-test-helper/grapes')
137
+ const utils = require('bfx-svc-test-helper/utils')
138
+
139
+ const grapes = createGrapes()
140
+
141
+ ;(async () => {
142
+ await grapes.start()
143
+ const port = utils.getGrapeApiPort(grapes)
144
+ console.log(port) // 30001
145
+ })()
146
+ ```
package/client.js ADDED
@@ -0,0 +1,89 @@
1
+ 'use strict'
2
+
3
+ const { PeerRPCClient } = require('grenache-nodejs-http')
4
+ const Link = require('grenache-nodejs-link')
5
+
6
+ class Client {
7
+ constructor (worker, opts = {}) {
8
+ this.peer = null
9
+ this.link = null
10
+
11
+ this.worker = worker
12
+ this.workerName = this.getWorkerName(worker)
13
+
14
+ const oDefault = {
15
+ grape: 'http://127.0.0.1:30001'
16
+ }
17
+
18
+ this.conf = { ...oDefault, ...opts }
19
+ }
20
+
21
+ getWorkerName (worker) {
22
+ if (typeof worker === 'string') {
23
+ return worker
24
+ }
25
+
26
+ return worker.name
27
+ }
28
+
29
+ connectGrape (cb = () => {}) {
30
+ return new Promise((resolve, reject) => {
31
+ if (this.link && this.peer) {
32
+ cb(null)
33
+ resolve()
34
+ return
35
+ }
36
+
37
+ this.link = new Link({
38
+ grape: this.conf.grape
39
+ })
40
+ this.link.start()
41
+
42
+ this.peer = new PeerRPCClient(this.link, this.conf)
43
+ this.peer.init()
44
+
45
+ resolve()
46
+ cb(null)
47
+ })
48
+ }
49
+
50
+ request (query, opts, cb) {
51
+ return new Promise((resolve, reject) => {
52
+ if (typeof opts === 'function') {
53
+ this.request(query, { timeout: 10000 }, opts)
54
+ return
55
+ }
56
+
57
+ this.connectGrape(() => {
58
+ this.peer.request(this.workerName, query, opts, (err, res) => {
59
+ if (err) {
60
+ if (cb) return cb(err)
61
+ reject(err)
62
+ return
63
+ }
64
+
65
+ if (cb) return cb(null, res)
66
+ resolve(res)
67
+ })
68
+ })
69
+ })
70
+ }
71
+
72
+ stop (cb) {
73
+ return new Promise((resolve, reject) => {
74
+ if (this.peer && this.link) {
75
+ this.peer.stop()
76
+ this.link.stop()
77
+ }
78
+
79
+ if (cb) return cb(null)
80
+ resolve()
81
+ })
82
+ }
83
+ }
84
+
85
+ function createClient (worker, opts) {
86
+ return new Client(worker, opts)
87
+ }
88
+
89
+ module.exports = createClient
@@ -0,0 +1,44 @@
1
+ 'use strict'
2
+
3
+ const createGrapes = require('./grapes')
4
+ const createClient = require('./client')
5
+ const createFxGrenache = require('./fauxgrenache')
6
+
7
+ const grapes = createGrapes()
8
+
9
+ const stubs = {
10
+ 'rest:util:net': {
11
+ getIpInfo (space, ip, cb) {
12
+ const res = [ip, { country: 'US', region: 'CA' }]
13
+ return cb(null, res)
14
+ }
15
+ }
16
+ }
17
+
18
+ ;(async () => {
19
+ await grapes.start()
20
+ // lets create a worker stub
21
+
22
+ // we can pass in Grape instances or an url
23
+ const url = 'http://localhost:30001'
24
+ const grape = grapes || url
25
+
26
+ const fxg = createFxGrenache(stubs, grape)
27
+ await fxg.start()
28
+ console.log('stub service started, announcing on grape')
29
+
30
+ const client = createClient('rest:util:net')
31
+ const res = await client.request({
32
+ action: 'getIpInfo',
33
+ args: ['8.8.8.8']
34
+ })
35
+
36
+ console.log('got response:', res)
37
+
38
+ await client.stop()
39
+ console.log('client stopped')
40
+ await fxg.stop()
41
+ console.log('grenache stub stopped')
42
+ await grapes.stop()
43
+ console.log('grapes stopped')
44
+ })()
@@ -0,0 +1,44 @@
1
+ 'use strict'
2
+
3
+ const createServer = require('./server')
4
+ const fetch = require('node-fetch')
5
+ const assert = require('assert')
6
+
7
+ const server = createServer({
8
+ reply: (reply) => {
9
+ reply({ fruit: 'lemon' }, 404) // default: 200
10
+ },
11
+
12
+ // all are optional
13
+ match: (payload, match) => {
14
+ match({
15
+ route: '/foo',
16
+ method: 'POST',
17
+ payload: (payload, cb) => {
18
+ return cb(null, payload.fruit === 'mango')
19
+ },
20
+ custom: (req, res, cb) => {
21
+ return cb(null, true)
22
+ }
23
+ })
24
+ },
25
+
26
+ // makes assertions on the _sent request_
27
+ test: (req, res, payload, cb) => {
28
+ assert.ok(payload.fruit, 'missing property')
29
+ cb(null)
30
+ }
31
+ }, { port: 1337 });
32
+
33
+ (async () => {
34
+ await server.start()
35
+
36
+ const res = await fetch('http://localhost:1337/foo', {
37
+ body: JSON.stringify({ fruit: 'mango' }),
38
+ method: 'POST'
39
+ })
40
+
41
+ console.log('response', await res.json(), res.status)
42
+
43
+ await server.stop()
44
+ })()
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ const createServer = require('./server')
4
+ const fetch = require('node-fetch')
5
+
6
+ const server = createServer([
7
+ {
8
+ reply: { animal: 'platypus' },
9
+ route: '/australia'
10
+ },
11
+ {
12
+ reply: { animal: 'Fair Isle Wren' },
13
+ route: '/england'
14
+ }
15
+ ], { port: 1337, debug: true });
16
+
17
+ (async () => {
18
+ await server.start()
19
+
20
+ const res = await fetch('http://localhost:1337/australia', {
21
+ body: JSON.stringify({ foo: 'bar' }),
22
+ method: 'POST'
23
+ })
24
+
25
+ console.log('response', await res.json(), res.status)
26
+
27
+ await server.stop()
28
+ })()
package/example.js ADDED
@@ -0,0 +1,41 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+
5
+ const createGrapes = require('./grapes')
6
+ const createWorker = require('./worker')
7
+ const createClient = require('./client')
8
+
9
+ const grapes = createGrapes()
10
+
11
+ ;(async () => {
12
+ await grapes.start()
13
+
14
+ const worker = createWorker({
15
+ env: 'development',
16
+ wtype: 'wrk-util-net-api',
17
+ apiPort: 8721,
18
+ serviceRoot: path.join(__dirname, '..', 'bfx-util-net-js')
19
+ }, grapes)
20
+
21
+ await worker.start()
22
+ console.log('worker started, announcing on grape')
23
+
24
+ // you can also pass 'rest:util:net'
25
+ const client = createClient(worker)
26
+
27
+ const res = await client.request({
28
+ action: 'getIpGeo',
29
+ args: ['8.8.8.8']
30
+ })
31
+
32
+ console.log('got response:')
33
+ console.log(res)
34
+
35
+ await client.stop()
36
+ console.log('client stopped')
37
+
38
+ await worker.stop()
39
+ console.log('worker stopped')
40
+ await grapes.stop()
41
+ })()
@@ -0,0 +1,148 @@
1
+ 'use strict'
2
+
3
+ const http = require('http')
4
+ const Link = require('grenache-nodejs-link')
5
+ const { getGrapeApiUrl } = require('./utils')
6
+
7
+ const getRawBody = require('raw-body')
8
+ const async = require('async')
9
+
10
+ class FauxGrenache {
11
+ constructor (stubs, grapes, opts = {}) {
12
+ this.stubs = stubs
13
+ this.grapes = grapes
14
+
15
+ this.port = opts.port || 1557
16
+ this.link = opts.link || null
17
+ }
18
+
19
+ getLink () {
20
+ if (this.link) {
21
+ return this.link
22
+ }
23
+
24
+ const url = getGrapeApiUrl(this.grapes)
25
+ this.link = new Link({
26
+ grape: url
27
+ })
28
+
29
+ return this.link
30
+ }
31
+
32
+ start (cb = () => {}) {
33
+ return new Promise((resolve, reject) => {
34
+ this.link = this.getLink()
35
+
36
+ async.waterfall([
37
+ (next) => {
38
+ this.link.start()
39
+ next()
40
+ },
41
+
42
+ (next) => {
43
+ this.listen(this.port, next)
44
+ },
45
+
46
+ (next) => {
47
+ this.announce(this.stubs, next)
48
+ }
49
+ ], (err) => {
50
+ if (err) {
51
+ reject(err)
52
+ cb(err)
53
+ return
54
+ }
55
+
56
+ resolve()
57
+ cb(null)
58
+ })
59
+ })
60
+ }
61
+
62
+ stop (cb = () => {}) {
63
+ return new Promise((resolve, reject) => {
64
+ async.waterfall([
65
+ (next) => {
66
+ this.stopAnnounces(this.stubs)
67
+ next()
68
+ },
69
+
70
+ (next) => {
71
+ this.link.stop()
72
+ next()
73
+ },
74
+
75
+ (next) => {
76
+ this.server.close(next)
77
+ }
78
+ ], (err) => {
79
+ if (err) {
80
+ reject(err)
81
+ cb(err)
82
+ return
83
+ }
84
+
85
+ resolve()
86
+ cb(null)
87
+ })
88
+ })
89
+ }
90
+
91
+ listen (port, cb) {
92
+ const stubs = this.stubs
93
+
94
+ // its just tests, so lets use some async/await
95
+ this.server = http.createServer(async (req, res) => {
96
+ const buf = await getRawBody(req)
97
+ const parsed = JSON.parse(buf.toString())
98
+
99
+ const [id, service, data] = parsed
100
+ if (!stubs[service]) {
101
+ throw new Error('ERR_TEST_MISSING_STUB')
102
+ }
103
+
104
+ if (!stubs[service][data.action]) {
105
+ throw new Error('ERR_TEST_MISSING_STUB_ACTION')
106
+ }
107
+
108
+ function reply (err, data) {
109
+ if (err) {
110
+ res.end(
111
+ `["${id}","ERR_API_BASE: ${err.message}",null]`
112
+ )
113
+ return
114
+ }
115
+
116
+ const d = JSON.stringify([id, null, data])
117
+ res.end(d)
118
+ }
119
+
120
+ data.args.push(reply)
121
+ const args = [{}, ...data.args]
122
+
123
+ stubs[service][data.action].apply(stubs[service], args)
124
+ }).listen(port, cb)
125
+ }
126
+
127
+ stopAnnounces (stubs) {
128
+ Object.keys(stubs).map((el) => {
129
+ this.link.stopAnnouncing(el, this.port)
130
+ })
131
+ }
132
+
133
+ announce (stubs, cb) {
134
+ const tasks = Object.keys(stubs).map((el) => {
135
+ return (cb) => {
136
+ this.link.startAnnouncing(el, this.port, cb)
137
+ }
138
+ })
139
+
140
+ async.parallel(tasks, cb)
141
+ }
142
+ }
143
+
144
+ function fauxGrenache (stubs, grapes, opts) {
145
+ return new FauxGrenache(stubs, grapes, opts)
146
+ }
147
+
148
+ module.exports = fauxGrenache
package/grapes.js ADDED
@@ -0,0 +1,118 @@
1
+ 'use strict'
2
+
3
+ const { Grape } = require('grenache-grape')
4
+ const waterfall = require('async/waterfall')
5
+ const EventEmitter = require('events')
6
+
7
+ class Grapes extends EventEmitter {
8
+ constructor (opts = {}) {
9
+ super()
10
+
11
+ this.opts = opts
12
+ this.grapes = this.addDefaultGrapes()
13
+ }
14
+
15
+ start (cb = () => {}) {
16
+ return new Promise((resolve, reject) => {
17
+ const tasks = this.grapes.map((grape, i) => {
18
+ return (cb) => {
19
+ if (i === 0) grape.once('ready', cb)
20
+ else grape.once('node', cb)
21
+
22
+ grape.start()
23
+ }
24
+ })
25
+
26
+ waterfall(tasks, (err) => {
27
+ if (err) {
28
+ this.emit('error')
29
+ reject(err)
30
+ return cb(err)
31
+ }
32
+ this.emit('ready')
33
+ resolve()
34
+ cb(null)
35
+ })
36
+ })
37
+ }
38
+
39
+ onAnnounce (serviceName, cb = () => {}) {
40
+ return new Promise((resolve, reject) => {
41
+ if (typeof serviceName === 'function') {
42
+ this.onAnnounce(null, serviceName)
43
+ return
44
+ }
45
+
46
+ if (!this.grapes[0]) {
47
+ this.on('ready', () => {
48
+ this.onAnnounce(serviceName, cb)
49
+ })
50
+ return
51
+ }
52
+
53
+ if (!serviceName) {
54
+ cb(null)
55
+ resolve()
56
+ return
57
+ }
58
+
59
+ this._checkAnnounce(serviceName, () => {
60
+ resolve()
61
+ cb(null)
62
+ })
63
+ })
64
+ }
65
+
66
+ _checkAnnounce (serviceName, cb) {
67
+ this.grapes[0].once('announce', (name) => {
68
+ if (serviceName === name) {
69
+ return cb(null)
70
+ }
71
+ this._checkAnnounce(serviceName, cb)
72
+ })
73
+ }
74
+
75
+ addDefaultGrapes () {
76
+ const { ports } = this.opts
77
+ const _ports = Array.isArray(ports)
78
+ ? ports
79
+ : [{
80
+ dht_port: 20002,
81
+ dht_bootstrap: ['127.0.0.1:20001'],
82
+ api_port: 40001
83
+ }, {
84
+ dht_port: 20001,
85
+ dht_bootstrap: ['127.0.0.1:20002'],
86
+ api_port: 30001
87
+ }]
88
+
89
+ return _ports
90
+ .map((ports) => new Grape(ports))
91
+ }
92
+
93
+ stop (cb = () => {}) {
94
+ return new Promise((resolve, reject) => {
95
+ const tasks = this.grapes.map((grape) => {
96
+ return (cb) => {
97
+ grape.stop(cb)
98
+ }
99
+ })
100
+
101
+ waterfall(tasks, (err) => {
102
+ if (err) {
103
+ reject(err)
104
+ return cb(err)
105
+ }
106
+
107
+ resolve()
108
+ cb(null)
109
+ })
110
+ })
111
+ }
112
+ }
113
+
114
+ function createGrapes (opts) {
115
+ return new Grapes(opts)
116
+ }
117
+
118
+ module.exports = createGrapes
package/index.js ADDED
@@ -0,0 +1,8 @@
1
+ 'use strict'
2
+
3
+ exports.grapes = require('./grapes')
4
+ exports.worker = require('./worker')
5
+ exports.client = require('./client')
6
+ exports.server = require('./server')
7
+ exports.utils = require('./utils')
8
+ exports.fxgrenache = require('./fauxgrenache')
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@bitfinex/bfx-svc-test-helper",
3
+ "version": "1.1.0",
4
+ "description": "common test functions",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "standard"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/bitfinexcom/bfx-svc-test-helper.git"
12
+ },
13
+ "author": "Robert <robert@bitfinex.com>",
14
+ "license": "Apache-2.0",
15
+ "devDependencies": {
16
+ "node-fetch": "^2.6.9",
17
+ "standard": "^17.0.0"
18
+ },
19
+ "dependencies": {
20
+ "@bitfinex/bfx-svc-boot-js": "git+https://github.com/bitfinexcom/bfx-svc-boot-js.git",
21
+ "async": "3.2.6",
22
+ "grenache-grape": "1.0.0",
23
+ "grenache-nodejs-http": "1.0.1",
24
+ "grenache-nodejs-link": "1.0.2",
25
+ "raw-body": "2.5.2"
26
+ }
27
+ }
package/server.js ADDED
@@ -0,0 +1,169 @@
1
+ 'use strict'
2
+
3
+ const http = require('http')
4
+ const async = require('async')
5
+
6
+ class Mocks {
7
+ constructor (routes, opts) {
8
+ this.routes = this.parseRouting(routes)
9
+ this.debug = opts.debug
10
+ }
11
+
12
+ parseRouting (routes) {
13
+ if (!Array.isArray(routes)) {
14
+ routes = [routes]
15
+ }
16
+
17
+ routes = routes.map((route) => {
18
+ if (route.route && route.match) {
19
+ throw new Error(
20
+ 'shorthand .route must be used without .match'
21
+ )
22
+ }
23
+
24
+ if (route.route) {
25
+ route.match = (data, match) => {
26
+ match({ route: route.route })
27
+ }
28
+ }
29
+
30
+ if (typeof route.reply !== 'function') {
31
+ const msg = route.reply
32
+ route.reply = (reply) => {
33
+ reply(msg, 200)
34
+ }
35
+ }
36
+
37
+ return route
38
+ })
39
+
40
+ return routes
41
+ }
42
+
43
+ getMock (req, res, data, cb) {
44
+ const match = (route, cb) => {
45
+ route.match(data, (filters) => {
46
+ if (filters.route && filters.route !== req.url) {
47
+ return cb(null, false)
48
+ }
49
+
50
+ if (filters.method && filters.method !== req.method) {
51
+ return cb(null, false)
52
+ }
53
+
54
+ if (!filters.custom) {
55
+ filters.custom = (req, res, cb) => {
56
+ return cb(null, true)
57
+ }
58
+ }
59
+
60
+ filters.custom(req, res, (err, res) => {
61
+ if (err) return cb(err)
62
+ if (res === false) return cb(null, false)
63
+
64
+ if (!filters.payload) return cb(null, res)
65
+
66
+ filters.payload(data, cb)
67
+ })
68
+ })
69
+ }
70
+
71
+ async.filter(this.routes, match, cb)
72
+ }
73
+
74
+ stubReply (req, res, mock) {
75
+ mock.reply((data, code) => {
76
+ if (code) {
77
+ res.statusCode = code
78
+ }
79
+
80
+ if (typeof data === 'object') {
81
+ data = JSON.stringify(data)
82
+ }
83
+
84
+ res.end(data)
85
+ })
86
+ }
87
+
88
+ handle (req, res) {
89
+ const data = []
90
+ req.on('data', (buf) => {
91
+ data.push(buf)
92
+ })
93
+
94
+ req.on('end', () => {
95
+ const str = Buffer.concat(data).toString()
96
+
97
+ this.debug && console.log('[DEBUG] bfx-svc-test-helper:', str)
98
+
99
+ let parsed
100
+ try {
101
+ parsed = JSON.parse(str)
102
+ } catch (e) {
103
+ parsed = str
104
+ }
105
+
106
+ this.getMock(req, res, parsed, (err, matches) => {
107
+ if (err) {
108
+ console.error('bfx-svc-test-helper: route matching error')
109
+ throw err
110
+ }
111
+
112
+ if (matches.length === 0) {
113
+ throw new Error(`bfx-svc-test-helper: no route matched (${req.url})`)
114
+ }
115
+
116
+ if (matches.length > 1) {
117
+ throw new Error(`bfx-svc-test-helper: multiple routes matched (${req.url})`)
118
+ }
119
+
120
+ const mock = matches[0]
121
+ if (!mock.test) mock.test = (req, res, payload, cb) => { cb(null) }
122
+
123
+ mock.test(req, res, parsed, (err) => {
124
+ if (err) {
125
+ console.error('bfx-svc-test-helper: client request test failed')
126
+ throw err
127
+ }
128
+
129
+ this.stubReply(req, res, mock)
130
+ })
131
+ })
132
+ })
133
+ }
134
+ }
135
+
136
+ class Server {
137
+ constructor (mocks, opts) {
138
+ this.opts = opts
139
+ this.mocks = mocks
140
+
141
+ this.server = null
142
+ this.port = this.opts.port
143
+ }
144
+
145
+ start (cb = () => {}) {
146
+ return new Promise((resolve, reject) => {
147
+ this.server = http.createServer(this.mocks.handle.bind(this.mocks)).listen(this.port, () => {
148
+ resolve()
149
+ cb(null)
150
+ })
151
+ })
152
+ }
153
+
154
+ stop (cb = () => {}) {
155
+ return new Promise((resolve, reject) => {
156
+ this.server.close(() => {
157
+ resolve()
158
+ cb(null)
159
+ })
160
+ })
161
+ }
162
+ }
163
+
164
+ function createServer (mocks, opts) {
165
+ const m = typeof mocks === 'function' ? mocks() : new Mocks(mocks, opts)
166
+ return new Server(m, opts)
167
+ }
168
+
169
+ module.exports = createServer
package/utils.js ADDED
@@ -0,0 +1,21 @@
1
+ 'use strict'
2
+
3
+ exports.getApi = getApi
4
+ function getApi (instance) {
5
+ return instance.worker.api_bfx.api
6
+ }
7
+
8
+ exports.getGrapeApiPort = getGrapeApiPort
9
+ function getGrapeApiPort (grapeHelperInstance) {
10
+ return grapeHelperInstance.grapes[0].conf.api_port
11
+ }
12
+
13
+ exports.getGrapeApiUrl = getGrapeApiUrl
14
+ function getGrapeApiUrl (grapes) {
15
+ if (typeof grapes === 'string') {
16
+ return grapes
17
+ }
18
+
19
+ const p = getGrapeApiPort(grapes)
20
+ return 'http://127.0.0.1:' + p
21
+ }
package/worker.js ADDED
@@ -0,0 +1,66 @@
1
+ 'use strict'
2
+
3
+ const worker = require('@bitfinex/bfx-svc-boot-js/lib/worker')
4
+
5
+ class Worker {
6
+ constructor (conf, grapes) {
7
+ this.worker = null
8
+ this.grenacheConf = null
9
+
10
+ this.conf = conf
11
+ this.grapes = grapes
12
+ this.name = null
13
+ }
14
+
15
+ start (cb = () => {}) {
16
+ return new Promise((resolve, reject) => {
17
+ this.worker = worker(this.conf)
18
+
19
+ if (!this.grapes) {
20
+ resolve()
21
+ cb(null)
22
+ return
23
+ }
24
+
25
+ if (!this.worker.getGrcConf) {
26
+ resolve()
27
+ return cb(null)
28
+ }
29
+
30
+ this.grenacheConf = this.worker.getGrcConf()
31
+
32
+ if (!this.grenacheConf.services) {
33
+ resolve()
34
+ return cb(null)
35
+ }
36
+
37
+ const [service] = this.grenacheConf.services
38
+ this.name = service
39
+ this.grapes.onAnnounce(service, () => {
40
+ resolve()
41
+ cb(null)
42
+ })
43
+ })
44
+ }
45
+
46
+ stop (cb = () => {}) {
47
+ return new Promise((resolve, reject) => {
48
+ this.worker.stop((err) => {
49
+ if (err) {
50
+ reject(err)
51
+ cb(err)
52
+ return
53
+ }
54
+
55
+ cb(null)
56
+ resolve()
57
+ })
58
+ })
59
+ }
60
+ }
61
+
62
+ function createWorker (opts, grapes) {
63
+ return new Worker(opts, grapes)
64
+ }
65
+
66
+ module.exports = createWorker