1 | 'use strict'
|
---|
2 |
|
---|
3 | const { kClients } = require('../core/symbols')
|
---|
4 | const Agent = require('../agent')
|
---|
5 | const {
|
---|
6 | kAgent,
|
---|
7 | kMockAgentSet,
|
---|
8 | kMockAgentGet,
|
---|
9 | kDispatches,
|
---|
10 | kIsMockActive,
|
---|
11 | kNetConnect,
|
---|
12 | kGetNetConnect,
|
---|
13 | kOptions,
|
---|
14 | kFactory
|
---|
15 | } = require('./mock-symbols')
|
---|
16 | const MockClient = require('./mock-client')
|
---|
17 | const MockPool = require('./mock-pool')
|
---|
18 | const { matchValue, buildMockOptions } = require('./mock-utils')
|
---|
19 | const { InvalidArgumentError, UndiciError } = require('../core/errors')
|
---|
20 | const Dispatcher = require('../dispatcher')
|
---|
21 | const Pluralizer = require('./pluralizer')
|
---|
22 | const PendingInterceptorsFormatter = require('./pending-interceptors-formatter')
|
---|
23 |
|
---|
24 | class FakeWeakRef {
|
---|
25 | constructor (value) {
|
---|
26 | this.value = value
|
---|
27 | }
|
---|
28 |
|
---|
29 | deref () {
|
---|
30 | return this.value
|
---|
31 | }
|
---|
32 | }
|
---|
33 |
|
---|
34 | class MockAgent extends Dispatcher {
|
---|
35 | constructor (opts) {
|
---|
36 | super(opts)
|
---|
37 |
|
---|
38 | this[kNetConnect] = true
|
---|
39 | this[kIsMockActive] = true
|
---|
40 |
|
---|
41 | // Instantiate Agent and encapsulate
|
---|
42 | if ((opts && opts.agent && typeof opts.agent.dispatch !== 'function')) {
|
---|
43 | throw new InvalidArgumentError('Argument opts.agent must implement Agent')
|
---|
44 | }
|
---|
45 | const agent = opts && opts.agent ? opts.agent : new Agent(opts)
|
---|
46 | this[kAgent] = agent
|
---|
47 |
|
---|
48 | this[kClients] = agent[kClients]
|
---|
49 | this[kOptions] = buildMockOptions(opts)
|
---|
50 | }
|
---|
51 |
|
---|
52 | get (origin) {
|
---|
53 | let dispatcher = this[kMockAgentGet](origin)
|
---|
54 |
|
---|
55 | if (!dispatcher) {
|
---|
56 | dispatcher = this[kFactory](origin)
|
---|
57 | this[kMockAgentSet](origin, dispatcher)
|
---|
58 | }
|
---|
59 | return dispatcher
|
---|
60 | }
|
---|
61 |
|
---|
62 | dispatch (opts, handler) {
|
---|
63 | // Call MockAgent.get to perform additional setup before dispatching as normal
|
---|
64 | this.get(opts.origin)
|
---|
65 | return this[kAgent].dispatch(opts, handler)
|
---|
66 | }
|
---|
67 |
|
---|
68 | async close () {
|
---|
69 | await this[kAgent].close()
|
---|
70 | this[kClients].clear()
|
---|
71 | }
|
---|
72 |
|
---|
73 | deactivate () {
|
---|
74 | this[kIsMockActive] = false
|
---|
75 | }
|
---|
76 |
|
---|
77 | activate () {
|
---|
78 | this[kIsMockActive] = true
|
---|
79 | }
|
---|
80 |
|
---|
81 | enableNetConnect (matcher) {
|
---|
82 | if (typeof matcher === 'string' || typeof matcher === 'function' || matcher instanceof RegExp) {
|
---|
83 | if (Array.isArray(this[kNetConnect])) {
|
---|
84 | this[kNetConnect].push(matcher)
|
---|
85 | } else {
|
---|
86 | this[kNetConnect] = [matcher]
|
---|
87 | }
|
---|
88 | } else if (typeof matcher === 'undefined') {
|
---|
89 | this[kNetConnect] = true
|
---|
90 | } else {
|
---|
91 | throw new InvalidArgumentError('Unsupported matcher. Must be one of String|Function|RegExp.')
|
---|
92 | }
|
---|
93 | }
|
---|
94 |
|
---|
95 | disableNetConnect () {
|
---|
96 | this[kNetConnect] = false
|
---|
97 | }
|
---|
98 |
|
---|
99 | // This is required to bypass issues caused by using global symbols - see:
|
---|
100 | // https://github.com/nodejs/undici/issues/1447
|
---|
101 | get isMockActive () {
|
---|
102 | return this[kIsMockActive]
|
---|
103 | }
|
---|
104 |
|
---|
105 | [kMockAgentSet] (origin, dispatcher) {
|
---|
106 | this[kClients].set(origin, new FakeWeakRef(dispatcher))
|
---|
107 | }
|
---|
108 |
|
---|
109 | [kFactory] (origin) {
|
---|
110 | const mockOptions = Object.assign({ agent: this }, this[kOptions])
|
---|
111 | return this[kOptions] && this[kOptions].connections === 1
|
---|
112 | ? new MockClient(origin, mockOptions)
|
---|
113 | : new MockPool(origin, mockOptions)
|
---|
114 | }
|
---|
115 |
|
---|
116 | [kMockAgentGet] (origin) {
|
---|
117 | // First check if we can immediately find it
|
---|
118 | const ref = this[kClients].get(origin)
|
---|
119 | if (ref) {
|
---|
120 | return ref.deref()
|
---|
121 | }
|
---|
122 |
|
---|
123 | // If the origin is not a string create a dummy parent pool and return to user
|
---|
124 | if (typeof origin !== 'string') {
|
---|
125 | const dispatcher = this[kFactory]('http://localhost:9999')
|
---|
126 | this[kMockAgentSet](origin, dispatcher)
|
---|
127 | return dispatcher
|
---|
128 | }
|
---|
129 |
|
---|
130 | // If we match, create a pool and assign the same dispatches
|
---|
131 | for (const [keyMatcher, nonExplicitRef] of Array.from(this[kClients])) {
|
---|
132 | const nonExplicitDispatcher = nonExplicitRef.deref()
|
---|
133 | if (nonExplicitDispatcher && typeof keyMatcher !== 'string' && matchValue(keyMatcher, origin)) {
|
---|
134 | const dispatcher = this[kFactory](origin)
|
---|
135 | this[kMockAgentSet](origin, dispatcher)
|
---|
136 | dispatcher[kDispatches] = nonExplicitDispatcher[kDispatches]
|
---|
137 | return dispatcher
|
---|
138 | }
|
---|
139 | }
|
---|
140 | }
|
---|
141 |
|
---|
142 | [kGetNetConnect] () {
|
---|
143 | return this[kNetConnect]
|
---|
144 | }
|
---|
145 |
|
---|
146 | pendingInterceptors () {
|
---|
147 | const mockAgentClients = this[kClients]
|
---|
148 |
|
---|
149 | return Array.from(mockAgentClients.entries())
|
---|
150 | .flatMap(([origin, scope]) => scope.deref()[kDispatches].map(dispatch => ({ ...dispatch, origin })))
|
---|
151 | .filter(({ pending }) => pending)
|
---|
152 | }
|
---|
153 |
|
---|
154 | assertNoPendingInterceptors ({ pendingInterceptorsFormatter = new PendingInterceptorsFormatter() } = {}) {
|
---|
155 | const pending = this.pendingInterceptors()
|
---|
156 |
|
---|
157 | if (pending.length === 0) {
|
---|
158 | return
|
---|
159 | }
|
---|
160 |
|
---|
161 | const pluralizer = new Pluralizer('interceptor', 'interceptors').pluralize(pending.length)
|
---|
162 |
|
---|
163 | throw new UndiciError(`
|
---|
164 | ${pluralizer.count} ${pluralizer.noun} ${pluralizer.is} pending:
|
---|
165 |
|
---|
166 | ${pendingInterceptorsFormatter.format(pending)}
|
---|
167 | `.trim())
|
---|
168 | }
|
---|
169 | }
|
---|
170 |
|
---|
171 | module.exports = MockAgent
|
---|