Skip to content

Commit 2a54482

Browse files
authored
feat(interceptor): duplicate query calls throw (#1630)
* feat(interceptor): duplicate query calls throw Continuation of #1626 BREAKING CHANGE: Attempting to call `Interceptor.query` twice throws an error.
1 parent f015929 commit 2a54482

File tree

3 files changed

+44
-42
lines changed

3 files changed

+44
-42
lines changed

lib/interceptor.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ function Interceptor(scope, uri, method, requestBody, interceptorOptions) {
5050
}${uri}`
5151
this.basePath = this.scope.basePath
5252
this.path = uriIsStr ? scope.basePathname + uri : uri
53+
this.queries = null
5354

5455
this.options = interceptorOptions || {}
5556
this.counter = 1
@@ -470,7 +471,9 @@ Interceptor.prototype.basicAuth = function basicAuth(options) {
470471
* nock('http://zombo.com').get('/').query({q: 't'});
471472
*/
472473
Interceptor.prototype.query = function query(queries) {
473-
this.queries = this.queries || {}
474+
if (this.queries !== null) {
475+
throw Error(`Query parameters have already been already defined`)
476+
}
474477

475478
// Allow all query strings to match this route
476479
if (queries === true) {
@@ -488,17 +491,18 @@ Interceptor.prototype.query = function query(queries) {
488491
strFormattingFn = common.percentDecode
489492
}
490493

491-
const entries =
492-
queries instanceof url.URLSearchParams ? queries : Object.entries(queries)
494+
if (queries instanceof url.URLSearchParams) {
495+
// Normalize the data into the shape that is matched against.
496+
// Duplicate keys are handled by combining the values into an array.
497+
queries = qs.parse(queries.toString())
498+
} else if (!_.isPlainObject(queries)) {
499+
throw Error(`Argument Error: ${queries}`)
500+
}
493501

494-
for (const [key, value] of entries) {
502+
this.queries = {}
503+
for (const [key, value] of Object.entries(queries)) {
495504
const formatted = common.formatQueryValue(key, value, strFormattingFn)
496505
const [formattedKey, formattedValue] = formatted
497-
498-
if (formattedKey in this.queries) {
499-
throw Error(`${formattedKey} already defined as a query parameter`)
500-
}
501-
502506
this.queries[formattedKey] = formattedValue
503507
}
504508

tests/test_allow_unmocked_https.js

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,35 +38,6 @@ test('Nock with allowUnmocked and an url match', async t => {
3838
server.close()
3939
})
4040

41-
test('Nock with allowUnmocked, url match and query false', async t => {
42-
const options = {
43-
key: fs.readFileSync('tests/ssl/ca.key'),
44-
cert: fs.readFileSync('tests/ssl/ca.crt'),
45-
}
46-
47-
const server = https
48-
.createServer(options, (req, res) => {
49-
res.writeHead(200)
50-
res.end(JSON.stringify({ status: 'default' }))
51-
})
52-
.listen(3000)
53-
54-
const url = `https://127.0.0.1:3000`
55-
56-
nock(`${url}`, { allowUnmocked: true })
57-
.get('/')
58-
.query(false)
59-
.reply(200, { status: 'intercepted' })
60-
61-
const { body } = await got(`${url}/otherpath`, {
62-
rejectUnauthorized: false,
63-
})
64-
65-
t.true(JSON.parse(body).status === 'default')
66-
67-
server.close()
68-
})
69-
7041
test('allow unmocked option works with https', t => {
7142
t.plan(6)
7243

tests/test_query.js

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,19 +121,46 @@ test('query() accepts URLSearchParams as input', async t => {
121121
scope.done()
122122
})
123123

124-
test('query() throws for duplicate keys', async t => {
124+
test('query() throws if query params have already been defined', async t => {
125+
const interceptor = nock('http://example.test').get('/?foo=bar')
126+
127+
t.throws(
128+
() => {
129+
interceptor.query({ foo: 'baz' })
130+
},
131+
{
132+
message: 'Query parameters have already been already defined',
133+
}
134+
)
135+
})
136+
137+
test('query() throws if query() was already called', async t => {
125138
const interceptor = nock('http://example.test')
126139
.get('/')
127140
.query({ foo: 'bar' })
128141

129142
t.throws(
130143
() => {
131-
interceptor.query({ foo: 'baz' })
144+
interceptor.query({ baz: 'qux' })
145+
},
146+
{
147+
message: 'Query parameters have already been already defined',
148+
}
149+
)
150+
})
151+
152+
test('query() throws for invalid arguments', t => {
153+
const interceptor = nock('http://example.test').get('/')
154+
155+
t.throws(
156+
() => {
157+
interceptor.query('foo=bar')
132158
},
133159
{
134-
message: 'foo already defined as a query parameter',
160+
message: 'Argument Error: foo=bar',
135161
}
136162
)
163+
t.done()
137164
})
138165

139166
test('query() matches a query string that contains special RFC3986 characters', t => {
@@ -304,7 +331,7 @@ test('query() will not match when a query string is present that was not registe
304331
.query({ foo: 'bar' })
305332
.reply(200)
306333

307-
mikealRequest('https://example.test/c?foo=bar&baz=foz', function(err, res) {
334+
mikealRequest('https://example.test/c?foo=bar&baz=foz', function(err) {
308335
t.equal(
309336
err.message.trim(),
310337
`Nock: No match for request ${JSON.stringify(

0 commit comments

Comments
 (0)