superset/superset-frontend/packages/superset-ui-core/test/connection/callApi/parseResponse.test.ts

191 lines
6.6 KiB
TypeScript
Raw Normal View History

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import fetchMock from 'fetch-mock';
import callApi from '../../../src/connection/callApi/callApi';
import parseResponse from '../../../src/connection/callApi/parseResponse';
import { LOGIN_GLOB } from '../fixtures/constants';
describe('parseResponse()', () => {
beforeAll(() => {
fetchMock.get(LOGIN_GLOB, { result: '1234' });
});
afterAll(fetchMock.restore);
const mockGetUrl = '/mock/get/url';
const mockPostUrl = '/mock/post/url';
const mockErrorUrl = '/mock/error/url';
const mockNoParseUrl = '/mock/noparse/url';
const mockGetPayload = { get: 'payload' };
const mockPostPayload = { post: 'payload' };
const mockErrorPayload = { status: 500, statusText: 'Internal error' };
fetchMock.get(mockGetUrl, mockGetPayload);
fetchMock.post(mockPostUrl, mockPostPayload);
fetchMock.get(mockErrorUrl, () => Promise.reject(mockErrorPayload));
fetchMock.get(mockNoParseUrl, new Response('test response'));
afterEach(fetchMock.reset);
it('returns a Promise', () => {
const apiPromise = callApi({ url: mockGetUrl, method: 'GET' });
const parsedResponsePromise = parseResponse(apiPromise);
expect(parsedResponsePromise).toBeInstanceOf(Promise);
});
it('resolves to { json, response } if the request succeeds', async () => {
expect.assertions(4);
const args = await parseResponse(
callApi({ url: mockGetUrl, method: 'GET' }),
);
expect(fetchMock.calls(mockGetUrl)).toHaveLength(1);
const keys = Object.keys(args);
expect(keys).toContain('response');
expect(keys).toContain('json');
expect(args.json).toEqual(
expect.objectContaining(mockGetPayload) as typeof args.json,
);
});
it('throws if `parseMethod=json` and .json() fails', async () => {
expect.assertions(3);
const mockTextUrl = '/mock/text/url';
const mockTextResponse =
'<html><head></head><body>I could be a stack trace or something</body></html>';
fetchMock.get(mockTextUrl, mockTextResponse);
2021-01-13 01:09:58 -05:00
let error;
try {
await parseResponse(callApi({ url: mockTextUrl, method: 'GET' }));
2021-01-13 01:09:58 -05:00
} catch (err) {
error = err as Error;
} finally {
expect(fetchMock.calls(mockTextUrl)).toHaveLength(1);
2021-01-13 01:09:58 -05:00
expect(error?.stack).toBeDefined();
expect(error?.message).toContain('Unexpected token');
}
});
it('resolves to { text, response } if the `parseMethod=text`', async () => {
expect.assertions(4);
// test with json + bigint to ensure that it was not first parsed as json
const mockTextParseUrl = '/mock/textparse/url';
const mockTextJsonResponse = '{ "value": 9223372036854775807 }';
fetchMock.get(mockTextParseUrl, mockTextJsonResponse);
const args = await parseResponse(
callApi({ url: mockTextParseUrl, method: 'GET' }),
'text',
);
expect(fetchMock.calls(mockTextParseUrl)).toHaveLength(1);
const keys = Object.keys(args);
expect(keys).toContain('response');
expect(keys).toContain('text');
expect(args.text).toBe(mockTextJsonResponse);
});
it('throws if parseMethod is not null|json|text', async () => {
expect.assertions(1);
2021-01-13 01:09:58 -05:00
let error;
try {
await parseResponse(
callApi({ url: mockNoParseUrl, method: 'GET' }),
'something-else' as never,
);
2021-01-13 01:09:58 -05:00
} catch (err) {
error = err;
} finally {
expect(error.message).toEqual(
expect.stringContaining('Expected parseResponse=json'),
);
}
});
it('resolves to unmodified `Response` object if `parseMethod=null|raw`', async () => {
expect.assertions(3);
const responseNull = await parseResponse(
callApi({ url: mockNoParseUrl, method: 'GET' }),
null,
);
const responseRaw = await parseResponse(
callApi({ url: mockNoParseUrl, method: 'GET' }),
'raw',
);
expect(fetchMock.calls(mockNoParseUrl)).toHaveLength(2);
expect(responseNull.bodyUsed).toBe(false);
expect(responseRaw.bodyUsed).toBe(false);
});
it('resolves to big number value if `parseMethod=json-bigint`', async () => {
const mockBigIntUrl = '/mock/get/bigInt';
const mockGetBigIntPayload =
'{ "value": 9223372036854775807, "minus": { "value": -483729382918228373892, "str": "something" }, "number": 1234, "floatValue": { "plus": 0.3452211361231223, "minus": -0.3452211361231223 } }';
fetchMock.get(mockBigIntUrl, mockGetBigIntPayload);
const responseBigNumber = await parseResponse(
callApi({ url: mockBigIntUrl, method: 'GET' }),
'json-bigint',
);
expect(`${responseBigNumber.json.value}`).toEqual('9223372036854775807');
expect(`${responseBigNumber.json.minus.value}`).toEqual(
'-483729382918228373892',
);
expect(responseBigNumber.json.number).toEqual(1234);
expect(responseBigNumber.json.floatValue.plus).toEqual(0.3452211361231223);
expect(responseBigNumber.json.floatValue.minus).toEqual(
-0.3452211361231223,
);
expect(
responseBigNumber.json.floatValue.plus +
responseBigNumber.json.floatValue.minus,
).toEqual(0);
expect(
responseBigNumber.json.floatValue.plus /
responseBigNumber.json.floatValue.minus,
).toEqual(-1);
expect(Math.min(responseBigNumber.json.floatValue.plus, 0)).toEqual(0);
expect(Math.abs(responseBigNumber.json.floatValue.minus)).toEqual(
responseBigNumber.json.floatValue.plus,
);
});
it('rejects if request.ok=false', async () => {
expect.assertions(3);
const mockNotOkayUrl = '/mock/notokay/url';
fetchMock.get(mockNotOkayUrl, 404); // 404s result in not response.ok=false
const apiPromise = callApi({ url: mockNotOkayUrl, method: 'GET' });
2021-01-13 01:09:58 -05:00
let error;
try {
await parseResponse(apiPromise);
2021-01-13 01:09:58 -05:00
} catch (err) {
error = err as { ok: boolean; status: number };
} finally {
expect(fetchMock.calls(mockNotOkayUrl)).toHaveLength(1);
2021-01-13 01:09:58 -05:00
expect(error?.ok).toBe(false);
expect(error?.status).toBe(404);
}
});
});