@Test
  public void testReplicaAdditionConfigUploadAndDelete() throws Exception {
    // Test bootstrap
    long expectedResourceCount =
        (testPartitionLayout.getPartitionLayout().getPartitionCount() - 1) / DEFAULT_MAX_PARTITIONS_PER_RESOURCE + 1;
    writeBootstrapOrUpgrade(expectedResourceCount, false);

    // Now, change the replica count for two partitions.
    Partition partition1 = (Partition) testPartitionLayout.getPartitionLayout()
        .getPartitions(null)
        .get(RANDOM.nextInt(testPartitionLayout.getPartitionCount()));
    Partition partition2 = (Partition) testPartitionLayout.getPartitionLayout()
        .getPartitions(null)
        .get(RANDOM.nextInt(testPartitionLayout.getPartitionCount()));

    // Add a new replica for partition1. Find a disk on a data node that does not already have a replica for partition1.
    HashSet<DataNodeId> partition1Nodes = new HashSet<>();
    for (ReplicaId replica : partition1.getReplicas()) {
      partition1Nodes.add(replica.getDataNodeId());
    }
    Disk diskForNewReplica;
    do {
      diskForNewReplica = testHardwareLayout.getRandomDisk();
    } while (partition1Nodes.contains(diskForNewReplica.getDataNode()));
    // Add new replica into partition1
    partition1.addReplica(new Replica(partition1, diskForNewReplica, testHardwareLayout.clusterMapConfig));
    String dcNameForNewReplica = diskForNewReplica.getDataNode().getDatacenterName();
    // Remove a replica from partition2.
    partition2.getReplicas().remove(0);

    Utils.writeJsonObjectToFile(zkJson, zkLayoutPath);
    Utils.writeJsonObjectToFile(testHardwareLayout.getHardwareLayout().toJSONObject(), hardwareLayoutPath);
    Utils.writeJsonObjectToFile(testPartitionLayout.getPartitionLayout().toJSONObject(), partitionLayoutPath);
    // test invalid admin type
    try {
      HelixBootstrapUpgradeUtil.uploadOrDeleteAdminConfigs(hardwareLayoutPath, partitionLayoutPath, zkLayoutPath,
          CLUSTER_NAME_PREFIX, dcStr, false, new String[]{"invalid_admin_type"}, null);
      fail("should fail because of invalid admin type");
    } catch (IllegalArgumentException e) {
      // expected
    }
    // upload replica addition admin config
    HelixBootstrapUpgradeUtil.uploadOrDeleteAdminConfigs(hardwareLayoutPath, partitionLayoutPath, zkLayoutPath,
        CLUSTER_NAME_PREFIX, dcStr, false, new String[]{ClusterMapUtils.REPLICA_ADDITION_STR}, null);
    // verify replica addition znode in Helix PropertyStore
    for (ZkInfo zkInfo : dcsToZkInfo.values()) {
      HelixPropertyStore<ZNRecord> propertyStore =
          CommonUtils.createHelixPropertyStore("localhost:" + zkInfo.getPort(), propertyStoreConfig,
              Collections.singletonList(propertyStoreConfig.rootPath));
      ZNRecord zNRecord = propertyStore.get(ClusterMapUtils.REPLICA_ADDITION_ZNODE_PATH, null, AccessOption.PERSISTENT);
      if (!activeDcSet.contains(zkInfo.getDcName())) {
        // if data center is not enabled, no admin config should be uploaded to Helix.
        assertNull(zNRecord);
      } else if (!zkInfo.getDcName().equals(dcNameForNewReplica)) {
        // if data center doesn't equal to
        Map<String, Map<String, String>> replicaAddition = zNRecord.getMapFields();
        assertTrue(replicaAddition.isEmpty());
      } else {
        assertNotNull(zNRecord);
        Map<String, Map<String, String>> replicaAddition = zNRecord.getMapFields();
        assertEquals("There should be only one partition in new replica map", 1, replicaAddition.size());
        assertNotNull("Partition id in new replica map is not expected",
            replicaAddition.get(partition1.toPathString()));
        Map<String, String> newReplicaMap = replicaAddition.get(partition1.toPathString());
        assertEquals("Partition class is not expected", partition1.getPartitionClass(),
            newReplicaMap.get(ClusterMapUtils.PARTITION_CLASS_STR));
        assertEquals("Replica capacity is not expected", String.valueOf(partition1.getReplicaCapacityInBytes()),
            newReplicaMap.get(ClusterMapUtils.REPLICAS_CAPACITY_STR));
        String instanceName =
            diskForNewReplica.getDataNode().getHostname() + "_" + diskForNewReplica.getDataNode().getPort();
        assertTrue("Instance name doesn't exist", newReplicaMap.containsKey(instanceName));
        String[] mountPathAndDiskCapacity = newReplicaMap.get(instanceName).split(DISK_CAPACITY_DELIM_STR);
        String mountPath = mountPathAndDiskCapacity[0];
        String diskCapacity = mountPathAndDiskCapacity[1];
        assertEquals("Mount path is not expected", diskForNewReplica.getMountPath(), mountPath);
        assertEquals("Disk capacity is not expected", diskForNewReplica.getRawCapacityInBytes(),
            Long.parseLong(diskCapacity));
      }
    }
    // test deleting replica addition config (failure case)
    try {
      HelixBootstrapUpgradeUtil.uploadOrDeleteAdminConfigs(hardwareLayoutPath, partitionLayoutPath, zkLayoutPath,
          CLUSTER_NAME_PREFIX, dcStr, true, new String[]{"invalid_admin_type"}, null);
      fail("should fail because of invalid admin type");
    } catch (IllegalArgumentException e) {
      // expected
    }
    // test deleting replica addition config (success case)
    HelixBootstrapUpgradeUtil.uploadOrDeleteAdminConfigs(hardwareLayoutPath, partitionLayoutPath, zkLayoutPath,
        CLUSTER_NAME_PREFIX, dcStr, true, new String[]{ClusterMapUtils.REPLICA_ADDITION_STR}, null);
    // verify config no longer exists
    for (ZkInfo zkInfo : dcsToZkInfo.values()) {
      HelixPropertyStore<ZNRecord> propertyStore =
          CommonUtils.createHelixPropertyStore("localhost:" + zkInfo.getPort(), propertyStoreConfig,
              Collections.singletonList(propertyStoreConfig.rootPath));
      ZNRecord zNRecord = propertyStore.get(ClusterMapUtils.REPLICA_ADDITION_ZNODE_PATH, null, AccessOption.PERSISTENT);
      assertNull("ZNode associated with admin config should not exist", zNRecord);
    }
  }
Test that new replicas can be correctly extracted from the diff between static clustermap and clustermap in Helix.
Verify that new added replica infos are correct (i.e. partition class, replica size, data node and mount path)
Also, this method tests replica addition config can be deleted correctly.
@throws Exception
ambry-tools/src/integration-test/java/com/github/ambry/clustermap/HelixBootstrapUpgradeToolTest.java