From 2a00c2c0f54f6707e182183db76906e7fb0631ba Mon Sep 17 00:00:00 2001 From: Oleksii Zubko Date: Thu, 13 May 2021 23:00:01 +0300 Subject: [PATCH] Integration test for MultiPartUpload. Adding two tests with mocked fetch. Tests run uploading for Blob V1 and Blob V2 and checks if the count of the requests are as expected. Resolves: OLPEDGE-2538 Relates-To: OLPEDGE-2456 Signed-off-by: Oleksii Zubko --- .../MultiPartUploadWrapper.test.ts | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 tests/integration/olp-sdk-dataservice-write/MultiPartUploadWrapper.test.ts diff --git a/tests/integration/olp-sdk-dataservice-write/MultiPartUploadWrapper.test.ts b/tests/integration/olp-sdk-dataservice-write/MultiPartUploadWrapper.test.ts new file mode 100644 index 00000000..8b51a43c --- /dev/null +++ b/tests/integration/olp-sdk-dataservice-write/MultiPartUploadWrapper.test.ts @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2021 HERE Europe B.V. + * + * Licensed 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. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ + +import * as chai from "chai"; +import sinonChai = require("sinon-chai"); +import sinon = require("sinon"); + +import { MultiPartUploadWrapper } from "@here/olp-sdk-dataservice-write"; +import { OlpClientSettings } from "@here/olp-sdk-core"; +import { FetchMock } from "../FetchMock"; + +chai.use(sinonChai); + +const assert = chai.assert; +const expect = chai.expect; + +describe("Multipart Upload Integration Test", function() { + let fetchMock: FetchMock; + let sandbox: sinon.SinonSandbox; + let fetchStub: sinon.SinonStub; + let settings: OlpClientSettings; + + settings = new OlpClientSettings({ + environment: "here", + getToken: () => Promise.resolve("mocked-token") + }); + const catalogHrn = "hrn:here:data:::mocked-hrn"; + const layerId = "mocked-layer-id"; + const dataSize = 74 * 1024 * 1024; + const contentType = "text/plain"; + const data = Buffer.alloc(dataSize, 1); + + const headers = new Headers(); + headers.append("cache-control", "max-age=3600"); + + before(function() { + sandbox = sinon.createSandbox(); + }); + + afterEach(function() { + sandbox.restore(); + }); + + beforeEach(function() { + fetchMock = new FetchMock(); + fetchStub = sandbox.stub(global as any, "fetch"); + fetchStub.callsFake(fetchMock.fetch()); + + settings = new OlpClientSettings({ + environment: "here", + getToken: () => Promise.resolve("mocked-token") + }); + }); + + it("Should initialize", function() { + const wrapper = new MultiPartUploadWrapper( + { + catalogHrn, + layerId, + handle: "mocked-dataHandle", + blobVersion: "v1", + contentType + }, + settings + ); + + assert.isDefined(wrapper); + expect(wrapper).be.instanceOf(MultiPartUploadWrapper); + }); + + it("Should upload to Blob V1", async function() { + const mockedResponses = new Map(); + + mockedResponses.set( + `https://api-lookup.data.api.platform.here.com/lookup/v1/resources/hrn:here:data:::mocked-hrn/apis`, + new Response( + JSON.stringify([ + { + api: "blob", + version: "v1", + baseURL: `https://mocked.api/blobstore/v1`, + parameters: {} + } + ]), + { headers } + ) + ); + + mockedResponses.set( + `https://mocked.api/blobstore/v1/layers/mocked-layer-id/data/mocked-dataHandle/multiparts`, + new Response( + JSON.stringify({ + links: { + status: { + href: `https://mocked.api/multiparts/mocked-blob-token`, + method: "GET" + }, + delete: { + href: `https://mocked.api/multiparts/mocked-blob-token`, + method: "DELETE" + }, + uploadPart: { + href: `https://mocked.api/multiparts/mocked-blob-token/parts`, + method: "POST" + }, + complete: { + href: `https://mocked.api/multiparts/mocked-blob-token`, + method: "PUT" + } + } + }), + { headers } + ) + ); + + const partsHeaders = new Headers(); + partsHeaders.append("cache-control", "max-age=3600"); + partsHeaders.append("ETag", `${Math.random() * 10000}`); + + for (let partNumber = 0; partNumber < 16; partNumber++) { + mockedResponses.set( + `https://mocked.api/multiparts/mocked-blob-token/parts?partNumber=${partNumber}`, + new Response(undefined, { headers: partsHeaders }) + ); + } + + mockedResponses.set( + `https://mocked.api/multiparts/mocked-blob-token`, + new Response(undefined, { headers }) + ); + + // Setup the fetch to use mocked responses. + fetchMock.withMockedResponses(mockedResponses); + + const wrapper = new MultiPartUploadWrapper( + { + catalogHrn, + layerId, + handle: "mocked-dataHandle", + blobVersion: "v1", + contentType + }, + settings + ); + + await wrapper.upload(data); + + /** + * Should be 18 calls: + * 1 - lookup + * 1 - start multipart + * 15 - upload part + * 1 - complete multipart + */ + expect(fetchStub.callCount).to.be.equal(18); + }); + + it("Should upload to Blob V2", async function() { + const mockedResponses = new Map(); + + mockedResponses.set( + `https://api-lookup.data.api.platform.here.com/lookup/v1/resources/hrn:here:data:::mocked-hrn/apis`, + new Response( + JSON.stringify([ + { + api: "blob", + version: "v2", + baseURL: `https://mocked.api/blobstore/v2`, + parameters: {} + } + ]), + { headers } + ) + ); + + mockedResponses.set( + `https://mocked.api/blobstore/v2/layers/mocked-layer-id/keys/mocked-key`, + new Response( + JSON.stringify({ + multipartToken: "mocked-multipartToken" + }), + { headers } + ) + ); + + for (let partNumber = 0; partNumber < 16; partNumber++) { + mockedResponses.set( + `https://mocked.api/blobstore/v2/layers/mocked-layer-id/keysMultipart/mocked-multipartToken/parts?partNumber=${partNumber}`, + new Response( + JSON.stringify({ + id: `${Math.random() * 10000}` + }), + { headers } + ) + ); + } + + mockedResponses.set( + `https://mocked.api/blobstore/v2/layers/mocked-layer-id/keysMultipart/mocked-multipartToken`, + new Response(undefined, { headers }) + ); + + // Setup the fetch to use mocked responses. + fetchMock.withMockedResponses(mockedResponses); + + const wrapper = new MultiPartUploadWrapper( + { + catalogHrn, + layerId, + handle: "mocked-key", + blobVersion: "v2", + contentType + }, + settings + ); + + await wrapper.upload(data); + + /** + * Should be 18 calls: + * 1 - lookup + * 1 - start multipart + * 15 - upload part + * 1 - complete multipart + */ + expect(fetchStub.callCount).to.be.equal(18); + }); +});