PR-URL: https://github.com/nodejs/node/pull/28525 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Roman Reiss <me@silverwind.io> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com> Reviewed-By: Jiawen Geng <technicalcute@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Daijiro Wachi <daijiro.wachi@gmail.com>
157 lines
4.1 KiB
JavaScript
157 lines
4.1 KiB
JavaScript
'use strict'
|
|
const common = require('./common-tap.js')
|
|
const Bluebird = require('bluebird')
|
|
const silentLogger = {
|
|
http: () => {},
|
|
silly: () => {}
|
|
}
|
|
|
|
const log = process.env.TAP_CHILD_ID
|
|
? silentLogger
|
|
: require('npmlog')
|
|
|
|
const http = require('http')
|
|
const EventEmitter = require('events')
|
|
// See fake-registry.md for details
|
|
|
|
class FakeRegistry extends EventEmitter {
|
|
constructor (opts) {
|
|
if (!opts) opts = {}
|
|
super(opts)
|
|
this.mocks = {}
|
|
this.port = opts.port || common.port
|
|
this.registry = 'http://localhost:' + this.port
|
|
this.server = http.createServer()
|
|
if (!opts.keepNodeAlive) this.server.unref()
|
|
this.server.on('request', (req, res) => {
|
|
if (this.mocks[req.method] && this.mocks[req.method][req.url]) {
|
|
this.mocks[req.method][req.url](req, res)
|
|
} else {
|
|
res.statusCode = 404
|
|
res.end(JSON.stringify({error: 'not found'}))
|
|
}
|
|
log.http('fake-registry', res.statusCode || 'unknown', '→', req.method, req.url)
|
|
})
|
|
this._error = err => {
|
|
log.silly('fake-registry', err)
|
|
this.emit('error', err)
|
|
}
|
|
this._addErrorHandler()
|
|
}
|
|
reset () {
|
|
this.mocks = {}
|
|
return this
|
|
}
|
|
close () {
|
|
this.reset()
|
|
this._removeErrorHandler()
|
|
return new Promise((resolve, reject) => {
|
|
this.server.once('error', reject)
|
|
this.server.once('close', () => {
|
|
this.removeListener('error', reject)
|
|
resolve(this)
|
|
})
|
|
this.server.close()
|
|
})
|
|
}
|
|
_addErrorHandler () {
|
|
this.server.on('error', this._error)
|
|
}
|
|
_removeErrorHandler () {
|
|
if (!this._error) return
|
|
this.server.removeListener('error', this._error)
|
|
}
|
|
listen (cb) {
|
|
this._removeErrorHandler()
|
|
return this._findPort(this.port).then(port => {
|
|
this._addErrorHandler()
|
|
this.port = port
|
|
this.registry = 'http://localhost:' + port
|
|
common.port = this.port
|
|
common.registry = this.registry
|
|
return this
|
|
}).asCallback(cb)
|
|
}
|
|
_findPort (port) {
|
|
return new Bluebird((resolve, reject) => {
|
|
let onListening
|
|
const onError = err => {
|
|
this.server.removeListener('listening', onListening)
|
|
if (err.code === 'EADDRINUSE') {
|
|
return resolve(this._findPort(++port))
|
|
} else {
|
|
return reject(err)
|
|
}
|
|
}
|
|
onListening = () => {
|
|
this.server.removeListener('error', onError)
|
|
resolve(port)
|
|
}
|
|
this.server.once('error', onError)
|
|
this.server.once('listening', onListening)
|
|
this.server.listen(port)
|
|
})
|
|
}
|
|
|
|
mock (method, url, respondWith) {
|
|
log.http('fake-registry', 'mock', method, url, respondWith)
|
|
if (!this.mocks[method]) this.mocks[method] = {}
|
|
if (typeof respondWith === 'function') {
|
|
this.mocks[method][url] = respondWith
|
|
} else if (Array.isArray(respondWith)) {
|
|
const [status, body] = respondWith
|
|
this.mocks[method][url] = (req, res) => {
|
|
res.statusCode = status
|
|
if (typeof body === 'object') {
|
|
res.end(JSON.stringify(body))
|
|
} else {
|
|
res.end(String(body))
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error('Invalid args, expected: mr.mock(method, url, [status, body])')
|
|
}
|
|
return this
|
|
}
|
|
|
|
// compat
|
|
done () {
|
|
this.reset()
|
|
}
|
|
filteringRequestBody () {
|
|
return this
|
|
}
|
|
post (url, matchBody) {
|
|
return this._createReply('POST', url)
|
|
}
|
|
get (url) {
|
|
return this._createReply('GET', url)
|
|
}
|
|
put (url, matchBody) {
|
|
return this._createReply('PUT', url)
|
|
}
|
|
delete (url) {
|
|
return this._createReply('DELETE', url)
|
|
}
|
|
_createReply (method, url) {
|
|
const mr = this
|
|
return {
|
|
twice: function () { return this },
|
|
reply: function (status, responseBody) {
|
|
mr.mock(method, url, [status, responseBody])
|
|
return mr
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new FakeRegistry()
|
|
module.exports.FakeRegistry = FakeRegistry
|
|
module.exports.compat = function (opts, cb) {
|
|
if (arguments.length === 1 && typeof opts === 'function') {
|
|
cb = opts
|
|
opts = {}
|
|
}
|
|
return new FakeRegistry(Object.assign({keepNodeAlive: true}, opts || {})).listen(cb)
|
|
}
|