/**
 * Licensed to jclouds, Inc. (jclouds) under one or more
 * contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  jclouds 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.
 */
package org.jclouds.aws.s3.blobstore.strategy.internal;

import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import static org.testng.Assert.fail;

import java.util.SortedMap;
import java.util.concurrent.TimeoutException;

import org.jclouds.aws.s3.AWSS3Client;
import org.jclouds.aws.s3.blobstore.AWSS3BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.blobstore.options.PutOptions;
import org.jclouds.io.MutableContentMetadata;
import org.jclouds.io.Payload;
import org.jclouds.io.PayloadSlicer;
import org.jclouds.rest.RestContext;
import org.jclouds.rest.internal.RestContextImpl;
import org.jclouds.s3.domain.ObjectMetadata;
import org.jclouds.s3.domain.ObjectMetadataBuilder;
import org.jclouds.util.Throwables2;
import org.testng.annotations.Test;

import com.google.common.collect.Maps;

/**
 * Tests behavior of {@code SequentialMultipartUploadStrategy}
 * 
 * @author Tibor Kiss
 */
@Test(groups = "unit")
public class SequentialMultipartUploadStrategyTest {
   
   @Test
   public void testWithTwoParts() {
      AWSS3BlobStore ablobStore = createMock(AWSS3BlobStore.class);      
      PayloadSlicer slicer = createMock(PayloadSlicer.class);
      String container = "container";
      String key = "mpu-test";
      Blob blob = createMock(Blob.class);
      MutableBlobMetadata blobMeta = createMock(MutableBlobMetadata.class);
      Payload payload = createMock(Payload.class);
      MutableContentMetadata contentMeta = createMock(MutableContentMetadata.class);
      BlobStoreContext context = createMock(BlobStoreContext.class);
      @SuppressWarnings("unchecked")
      RestContext<Object, Object> psc = createMock(RestContextImpl.class);
      AWSS3Client client = createMock(AWSS3Client.class);
      ObjectMetadata ometa = createMock(ObjectMetadata.class);
      String uploadId = "uploadId";
      long chunkSize = MultipartUploadSlicingAlgorithm.DEFAULT_PART_SIZE;
      long remaining = 100L;
      SortedMap<Integer, String> etags = Maps.newTreeMap();
      etags.put(new Integer(1), "eTag1");
      etags.put(new Integer(2), "eTag2");
      
      expect(blob.getMetadata()).andReturn(blobMeta).atLeastOnce();
      expect(blobMeta.getName()).andReturn(key).atLeastOnce();
      expect(blob.getPayload()).andReturn(payload).atLeastOnce();
      expect(payload.getContentMetadata()).andReturn(contentMeta).atLeastOnce();
      expect(contentMeta.getContentLength()).andReturn(new Long(chunkSize + remaining));
      expect(ablobStore.getContext()).andReturn(context).atLeastOnce();
      expect(context.getProviderSpecificContext()).andReturn(psc).atLeastOnce();
      expect(psc.getApi()).andReturn(client).atLeastOnce();
      expect(client.initiateMultipartUpload(container, new ObjectMetadataBuilder().key(key).build())).andReturn("uploadId").atLeastOnce();
      expect(slicer.slice(payload, 0, chunkSize)).andReturn(payload).atLeastOnce();
      expect(client.uploadPart(container, key, 1, uploadId, payload)).andReturn("eTag1").atLeastOnce();
      expect(slicer.slice(payload, chunkSize, remaining)).andReturn(payload).atLeastOnce();
      expect(client.uploadPart(container, key, 2, uploadId, payload)).andReturn("eTag2").atLeastOnce();      
      expect(client.completeMultipartUpload(container, key, uploadId, etags)).andReturn("eTag").atLeastOnce();

      replay(ablobStore);
      replay(slicer);
      replay(blob);
      replay(blobMeta);
      replay(payload);
      replay(contentMeta);
      replay(context);
      replay(psc);
      replay(client);
      replay(ometa);

      SequentialMultipartUploadStrategy strategy = new SequentialMultipartUploadStrategy(ablobStore, slicer);
      strategy.execute(container, blob, PutOptions.NONE);

      verify(ablobStore);
      verify(slicer);
      verify(blob);
      verify(blobMeta);
      verify(payload);
      verify(contentMeta);
      verify(context);
      verify(psc);
      verify(client);
      verify(ometa);
   }

   @Test
   public void testWithTimeout() {
      AWSS3BlobStore ablobStore = createMock(AWSS3BlobStore.class);      
      PayloadSlicer slicer = createMock(PayloadSlicer.class);
      String container = "container";
      String key = "mpu-test";
      Blob blob = createMock(Blob.class);
      MutableBlobMetadata blobMeta = createMock(MutableBlobMetadata.class);
      Payload payload = createMock(Payload.class);
      MutableContentMetadata contentMeta = createMock(MutableContentMetadata.class);
      BlobStoreContext context = createMock(BlobStoreContext.class);
      @SuppressWarnings("unchecked")
      RestContext<Object, Object> psc = createMock(RestContextImpl.class);
      AWSS3Client client = createMock(AWSS3Client.class);
      ObjectMetadata ometa = createMock(ObjectMetadata.class);
      String uploadId = "uploadId";
      long chunkSize = MultipartUploadSlicingAlgorithm.DEFAULT_PART_SIZE;
      long remaining = 100L;
      SortedMap<Integer, String> etags = Maps.newTreeMap();
      etags.put(new Integer(1), "eTag1");
      etags.put(new Integer(2), "eTag2");
      
      expect(blob.getMetadata()).andReturn(blobMeta).atLeastOnce();
      expect(blobMeta.getName()).andReturn(key).atLeastOnce();
      expect(blob.getPayload()).andReturn(payload).atLeastOnce();
      expect(payload.getContentMetadata()).andReturn(contentMeta).atLeastOnce();
      expect(contentMeta.getContentLength()).andReturn(new Long(chunkSize + remaining));
      expect(ablobStore.getContext()).andReturn(context).atLeastOnce();
      expect(context.getProviderSpecificContext()).andReturn(psc).atLeastOnce();
      expect(psc.getApi()).andReturn(client).atLeastOnce();
      expect(client.initiateMultipartUpload(container, new ObjectMetadataBuilder().key(key).build())).andReturn("uploadId").atLeastOnce();
      expect(slicer.slice(payload, 0, chunkSize)).andReturn(payload).atLeastOnce();
      expect(client.uploadPart(container, key, 1, uploadId, payload)).andReturn("eTag1").atLeastOnce();
      expect(slicer.slice(payload, chunkSize, remaining)).andReturn(payload).atLeastOnce();
      expect(client.uploadPart(container, key, 2, uploadId, payload)).andThrow(new RuntimeException(new TimeoutException()));
      client.abortMultipartUpload(container, key, uploadId);
      expectLastCall().atLeastOnce();

      replay(ablobStore);
      replay(slicer);
      replay(blob);
      replay(blobMeta);
      replay(payload);
      replay(contentMeta);
      replay(context);
      replay(psc);
      replay(client);
      replay(ometa);

      SequentialMultipartUploadStrategy strategy = new SequentialMultipartUploadStrategy(ablobStore, slicer);
      try {
         strategy.execute(container, blob, PutOptions.NONE);
         fail("Should throw RuntimeException with TimeoutException cause!");
      } catch (RuntimeException rtex) {
         TimeoutException timeout = Throwables2.getFirstThrowableOfType(rtex, TimeoutException.class);
         if (timeout == null) {
            throw rtex;
         }
      }

      verify(ablobStore);
      verify(slicer);
      verify(blob);
      verify(blobMeta);
      verify(payload);
      verify(contentMeta);
      verify(context);
      verify(psc);
      verify(client);
      verify(ometa);
   }
}
