/**
 * 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.
 */

package org.apache.hadoop.fs.azure;

import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeNotNull;

import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.security.UserGroupInformation;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.microsoft.azure.storage.blob.BlobOutputStream;
import com.microsoft.azure.storage.blob.CloudBlockBlob;

public class TestOutOfBandAzureBlobOperationsLive {
  private FileSystem fs;
  private AzureBlobStorageTestAccount testAccount;

  @Before
  public void setUp() throws Exception {
    testAccount = AzureBlobStorageTestAccount.create();
    if (testAccount != null) {
      fs = testAccount.getFileSystem();
    }
    assumeNotNull(testAccount);
  }

  @After
  public void tearDown() throws Exception {
    if (testAccount != null) {
      testAccount.cleanup();
      testAccount = null;
      fs = null;
    }
  }

  // scenario for this particular test described at MONARCH-HADOOP-764
  // creating a file out-of-band would confuse mkdirs("<oobfilesUncleFolder>")
  // eg oob creation of "user/<name>/testFolder/a/input/file"
  // Then wasb creation of "user/<name>/testFolder/a/output" fails
  @Test
  public void outOfBandFolder_uncleMkdirs() throws Exception {

    // NOTE: manual use of CloubBlockBlob targets working directory explicitly.
    // WASB driver methods prepend working directory implicitly.
    String workingDir = "user/"
        + UserGroupInformation.getCurrentUser().getShortUserName() + "/";

    CloudBlockBlob blob = testAccount.getBlobReference(workingDir
        + "testFolder1/a/input/file");
    BlobOutputStream s = blob.openOutputStream();
    s.close();
    assertTrue(fs.exists(new Path("testFolder1/a/input/file")));

    Path targetFolder = new Path("testFolder1/a/output");
    assertTrue(fs.mkdirs(targetFolder));
  }

  // scenario for this particular test described at MONARCH-HADOOP-764
  @Test
  public void outOfBandFolder_parentDelete() throws Exception {

    // NOTE: manual use of CloubBlockBlob targets working directory explicitly.
    // WASB driver methods prepend working directory implicitly.
    String workingDir = "user/"
        + UserGroupInformation.getCurrentUser().getShortUserName() + "/";
    CloudBlockBlob blob = testAccount.getBlobReference(workingDir
        + "testFolder2/a/input/file");
    BlobOutputStream s = blob.openOutputStream();
    s.close();
    assertTrue(fs.exists(new Path("testFolder2/a/input/file")));

    Path targetFolder = new Path("testFolder2/a/input");
    assertTrue(fs.delete(targetFolder, true));
  }

  @Test
  public void outOfBandFolder_rootFileDelete() throws Exception {

    CloudBlockBlob blob = testAccount.getBlobReference("fileY");
    BlobOutputStream s = blob.openOutputStream();
    s.close();
    assertTrue(fs.exists(new Path("/fileY")));
    assertTrue(fs.delete(new Path("/fileY"), true));
  }

  @Test
  public void outOfBandFolder_firstLevelFolderDelete() throws Exception {

    CloudBlockBlob blob = testAccount.getBlobReference("folderW/file");
    BlobOutputStream s = blob.openOutputStream();
    s.close();
    assertTrue(fs.exists(new Path("/folderW")));
    assertTrue(fs.exists(new Path("/folderW/file")));
    assertTrue(fs.delete(new Path("/folderW"), true));
  }

  // scenario for this particular test described at MONARCH-HADOOP-764
  @Test
  public void outOfBandFolder_siblingCreate() throws Exception {

    // NOTE: manual use of CloubBlockBlob targets working directory explicitly.
    // WASB driver methods prepend working directory implicitly.
    String workingDir = "user/"
        + UserGroupInformation.getCurrentUser().getShortUserName() + "/";
    CloudBlockBlob blob = testAccount.getBlobReference(workingDir
        + "testFolder3/a/input/file");
    BlobOutputStream s = blob.openOutputStream();
    s.close();
    assertTrue(fs.exists(new Path("testFolder3/a/input/file")));

    Path targetFile = new Path("testFolder3/a/input/file2");
    FSDataOutputStream s2 = fs.create(targetFile);
    s2.close();
  }

  // scenario for this particular test described at MONARCH-HADOOP-764
  // creating a new file in the root folder
  @Test
  public void outOfBandFolder_create_rootDir() throws Exception {
    Path targetFile = new Path("/newInRoot");
    FSDataOutputStream s2 = fs.create(targetFile);
    s2.close();
  }

  // scenario for this particular test described at MONARCH-HADOOP-764
  @Test
  public void outOfBandFolder_rename() throws Exception {

    // NOTE: manual use of CloubBlockBlob targets working directory explicitly.
    // WASB driver methods prepend working directory implicitly.
    String workingDir = "user/"
        + UserGroupInformation.getCurrentUser().getShortUserName() + "/";
    CloudBlockBlob blob = testAccount.getBlobReference(workingDir
        + "testFolder4/a/input/file");
    BlobOutputStream s = blob.openOutputStream();
    s.close();

    Path srcFilePath = new Path("testFolder4/a/input/file");
    assertTrue(fs.exists(srcFilePath));

    Path destFilePath = new Path("testFolder4/a/input/file2");
    fs.rename(srcFilePath, destFilePath);
  }

  // Verify that you can rename a file which is the only file in an implicit folder in the
  // WASB file system.
  // scenario for this particular test described at MONARCH-HADOOP-892
  @Test
  public void outOfBandSingleFile_rename() throws Exception {

    //NOTE: manual use of CloubBlockBlob targets working directory explicitly.
    //       WASB driver methods prepend working directory implicitly.
    String workingDir = "user/" + UserGroupInformation.getCurrentUser().getShortUserName() + "/";
    CloudBlockBlob blob = testAccount.getBlobReference(workingDir + "testFolder5/a/input/file");
    BlobOutputStream s = blob.openOutputStream();
    s.close();

    Path srcFilePath = new Path("testFolder5/a/input/file");
    assertTrue(fs.exists(srcFilePath));

    Path destFilePath = new Path("testFolder5/file2");
    fs.rename(srcFilePath, destFilePath);
  }

  // WASB must force explicit parent directories in create, delete, mkdirs, rename.
  // scenario for this particular test described at MONARCH-HADOOP-764
  @Test
  public void outOfBandFolder_rename_rootLevelFiles() throws Exception {

    // NOTE: manual use of CloubBlockBlob targets working directory explicitly.
    // WASB driver methods prepend working directory implicitly.
    CloudBlockBlob blob = testAccount.getBlobReference("fileX");
    BlobOutputStream s = blob.openOutputStream();
    s.close();

    Path srcFilePath = new Path("/fileX");
    assertTrue(fs.exists(srcFilePath));

    Path destFilePath = new Path("/fileXrename");
    fs.rename(srcFilePath, destFilePath);
  }
}
