diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts index e645e421..a119447b 100644 --- a/src/utils/hooks.ts +++ b/src/utils/hooks.ts @@ -6,10 +6,13 @@ type HookCallback = (value: V) => V | Promise function wrapRequest(request: AxiosRequestConfig): BeeRequest { let headers = request.headers - // For request interceptor, axios returns wrapped headers split based on methods - // which needs to be joined. This is not case for response interceptor's config property which has headers already "flat". + // For request interceptor, axios returns also default headers in the object that needs to be joined based on HTTP method + // and the default headers removed if (headers.common && headers.get && headers.delete && headers.post && headers.patch) { - headers = Object.assign({}, request.headers.common ?? {}, request.headers[request.method as HttpMethod] ?? {}) + // Filters out the default headers properties + const { common: _1, get: _2, delete: _3, post: _4, patch: _5, head: _6, put: _7, ...rest } = headers + + headers = Object.assign({}, request.headers.common ?? {}, request.headers[request.method as HttpMethod] ?? {}, rest) } return { diff --git a/test/unit/nock.ts b/test/unit/nock.ts index 94ae9f01..dd1ed595 100644 --- a/test/unit/nock.ts +++ b/test/unit/nock.ts @@ -7,6 +7,7 @@ export const MOCK_SERVER_URL = 'http://localhost:12345/' // Endpoints const FEED_ENDPOINT = '/feeds' +const BZZ_ENDPOINT = '/bzz' const BYTES_ENDPOINT = '/bytes' const POSTAGE_ENDPOINT = '/stamps' const CHEQUEBOOK_ENDPOINT = '/chequebook' @@ -29,6 +30,33 @@ export function downloadDataMock(reference: Reference | string): nock.Intercepto return nock(MOCK_SERVER_URL).get(`${BYTES_ENDPOINT}/${reference}`) } +interface UploadOptions { + name?: string + tag?: number + pin?: boolean + encrypt?: boolean + collection?: boolean + indexDocument?: string + errorDocument?: string +} + +function camelCaseToDashCase(str: string) { + return str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`) +} + +export function uploadFileMock(batchId: string, name?: string, options?: UploadOptions): nock.Interceptor { + // Prefixes the options with `swarm-` so the object can be used for required headers + const headers = Object.entries(options || {}).reduce>((prev, curr) => { + prev[`swarm-${camelCaseToDashCase(curr[0])}`] = curr[1] + + return prev + }, {}) + + return nock(MOCK_SERVER_URL, { reqheaders: { 'swarm-postage-batch-id': batchId, ...headers } }) + .post(`${BZZ_ENDPOINT}`) + .query({ name }) +} + export function createPostageBatchMock( amount: string, depth: string, diff --git a/test/unit/utils/hooks.spec.ts b/test/unit/utils/hooks.spec.ts index 4aec7e4a..4fdf4d2b 100644 --- a/test/unit/utils/hooks.spec.ts +++ b/test/unit/utils/hooks.spec.ts @@ -1,5 +1,5 @@ -import { assertAllIsDone, downloadDataMock, fetchFeedUpdateMock, MOCK_SERVER_URL } from '../nock' -import { testChunkHash, testIdentity, testJsonHash } from '../../utils' +import { assertAllIsDone, downloadDataMock, fetchFeedUpdateMock, MOCK_SERVER_URL, uploadFileMock } from '../nock' +import { testBatchId, testChunkHash, testIdentity, testJsonHash } from '../../utils' import { Bee, ReferenceResponse } from '../../../src' import * as hooks from '../../../src/utils/hooks' @@ -115,4 +115,65 @@ describe('hooks', () => { assertAllIsDone() }) + + it('should call with request with correct headers', async () => { + const bee = new Bee(MOCK_SERVER_URL) + + uploadFileMock(testBatchId, 'nice.txt', { encrypt: true }).reply(200, { + reference: testJsonHash, + } as ReferenceResponse) + + const requestSpy = jest.fn() + const responseSpy = jest.fn() + hooks.onRequest(requestSpy) + hooks.onResponse(responseSpy) + + const reference = await bee.uploadFile(testBatchId, 'hello world', 'nice.txt', { encrypt: true }) + + expect(reference).toEqual(testJsonHash) + + expect(requestSpy.mock.calls.length).toEqual(1) + expect(requestSpy.mock.calls[0].length).toEqual(1) + expect(requestSpy.mock.calls[0][0]).toEqual({ + url: `${MOCK_SERVER_URL}bzz`, + method: 'post', + data: new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]), + params: { name: 'nice.txt' }, + headers: { + Accept: 'application/json, text/plain, */*', + 'Content-Type': 'application/x-www-form-urlencoded', + 'swarm-encrypt': 'true', + 'swarm-postage-batch-id': testBatchId, + }, + }) + + expect(responseSpy.mock.calls.length).toEqual(1) + expect(responseSpy.mock.calls[0].length).toEqual(1) + expect(responseSpy.mock.calls[0][0]).toEqual({ + status: 200, + statusText: null, + headers: { + 'content-type': 'application/json', + }, + data: { + reference: testJsonHash, + }, + request: { + url: `${MOCK_SERVER_URL}bzz`, + method: 'post', + data: new ArrayBuffer(0), + params: { name: 'nice.txt' }, + headers: { + Accept: 'application/json, text/plain, */*', + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': 'axios/0.21.1', + 'Content-Length': 11, + 'swarm-encrypt': 'true', + 'swarm-postage-batch-id': testBatchId, + }, + }, + }) + + assertAllIsDone() + }) })