diff --git a/src/services/http.js b/src/services/http.js index 0b92af3..ceac603 100644 --- a/src/services/http.js +++ b/src/services/http.js @@ -210,7 +210,7 @@ export const createAccessToken = async (config, httpClient, body) => { access = await fetch( accessUrl, { - method: 'post', + method: 'POST', timeout: config.timeout, agent: config.agent, body: new URLSearchParams(requestBody), @@ -236,7 +236,10 @@ export const createAccessToken = async (config, httpClient, body) => { }; -const httpRequest = async (httpClient, method, path, config, auth, request, idempotencyKey) => { +const httpRequest = async (httpClient, verb, path, config, auth, request, idempotencyKey) => { + // fetch only normalises the case of DELETE, GET, HEAD, OPTIONS, POST and PUT, so a + // lowercase "patch" goes out as-is and the API answers 400. Send every verb uppercase. + const method = String(verb).toUpperCase(); let authHeader = null; if (auth) { diff --git a/test/http/httpMethod.js b/test/http/httpMethod.js new file mode 100644 index 0000000..aa1a5a8 --- /dev/null +++ b/test/http/httpMethod.js @@ -0,0 +1,53 @@ +import { expect } from 'chai'; +import http from 'http'; +import { get, post, patch, put, _delete } from '../../src/services/http.js'; + +// nock matches the method case-insensitively, so it cannot catch a verb sent in +// lowercase. These tests use a real local server and read the method as it +// arrives on the wire, for both HTTP clients. +describe('HTTP method on the wire', () => { + let server; + let baseUrl; + let received; + + before((done) => { + server = http.createServer((req, res) => { + received.push(req.method); + req.resume(); + req.on('end', () => { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end('{}'); + }); + }); + server.listen(0, '127.0.0.1', () => { + baseUrl = `http://127.0.0.1:${server.address().port}`; + done(); + }); + }); + + after((done) => { + server.close(done); + }); + + beforeEach(() => { + received = []; + }); + + const config = { headers: {} }; + const calls = [ + ['GET', (client) => get(client, `${baseUrl}/x`, config, 'Bearer sk_test')], + ['POST', (client) => post(client, `${baseUrl}/x`, config, 'Bearer sk_test', { a: 1 })], + ['PATCH', (client) => patch(client, `${baseUrl}/x`, config, 'Bearer sk_test', { a: 1 })], + ['PUT', (client) => put(client, `${baseUrl}/x`, config, 'Bearer sk_test', { a: 1 })], + ['DELETE', (client) => _delete(client, `${baseUrl}/x`, config, 'Bearer sk_test')], + ]; + + for (const client of ['fetch', 'axios']) { + for (const [verb, call] of calls) { + it(`sends ${verb} in uppercase with the ${client} client`, async () => { + await call(client); + expect(received).to.deep.equal([verb]); + }); + } + } +});