From b6566e0c13c55332005dcb598d9ac9996e284f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:37:54 +0200 Subject: [PATCH] fix(http): send every HTTP verb in uppercase The default fetch client passed the method in lowercase. fetch only normalises DELETE, GET, HEAD, OPTIONS, POST and PUT, so PATCH went out as "patch" and the API answered 400. Every PATCH operation failed with the default client; axios was not affected. The method is now uppercased once in httpRequest for both clients. New tests read the method on a local server for GET, POST, PATCH, PUT and DELETE with fetch and axios, since nock matches the method case-insensitively and could not catch this. --- src/services/http.js | 7 ++++-- test/http/httpMethod.js | 53 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 test/http/httpMethod.js diff --git a/src/services/http.js b/src/services/http.js index 0b92af38..ceac6037 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 00000000..aa1a5a81 --- /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]); + }); + } + } +});