@Test
  public void undeleteLifeVersionTest() throws StoreException, IOException {
    short lifeVersion = 2;
    MockId addedId = put(1, PUT_RECORD_SIZE, Utils.Infinite_Time, Utils.getRandomShort(TestUtils.RANDOM),
        Utils.getRandomShort(TestUtils.RANDOM), lifeVersion).get(0);

    // Undelete with -1 as lifeVersion since it's not deleted, should fail
    MessageInfo info = new MessageInfo(addedId, UNDELETE_RECORD_SIZE, false, false, true, Utils.Infinite_Time, null,
        addedId.getAccountId(), addedId.getContainerId(), time.milliseconds(), MessageInfo.LIFE_VERSION_FROM_FRONTEND);
    try {
      store.undelete(info);
      fail("Should not succeed");
    } catch (StoreException e) {
      assertEquals(e.getErrorCode(), StoreErrorCodes.IDNotDeleted);
    }

    // Undelete with smaller lifeVersion, should fail
    info = new MessageInfo(addedId, UNDELETE_RECORD_SIZE, false, false, true, Utils.Infinite_Time, null,
        addedId.getAccountId(), addedId.getContainerId(), time.milliseconds(), (short) (lifeVersion - 1));
    try {
      store.undelete(info);
      fail("Should not succeed");
    } catch (StoreException e) {
      assertEquals(e.getErrorCode(), StoreErrorCodes.LifeVersionConflict);
    }

    // Undelete with same lifeVersion, should fail
    info = new MessageInfo(addedId, UNDELETE_RECORD_SIZE, false, false, true, Utils.Infinite_Time, null,
        addedId.getAccountId(), addedId.getContainerId(), time.milliseconds(), lifeVersion);
    try {
      store.undelete(info);
      fail("Should not succeed");
    } catch (StoreException e) {
      assertEquals(e.getErrorCode(), StoreErrorCodes.LifeVersionConflict);
    }

    // Undelete with larger lifeVersion, should succeed.
    info = new MessageInfo(addedId, UNDELETE_RECORD_SIZE, false, false, true, Utils.Infinite_Time, null,
        addedId.getAccountId(), addedId.getContainerId(), time.milliseconds(), (short) (lifeVersion + 2));
    store.undelete(info);
    deletedKeys.remove(addedId);
    liveKeys.add(addedId);
    undeletedKeys.add(addedId);
    StoreInfo storeInfo = store.get(Arrays.asList(addedId), EnumSet.noneOf(StoreGetOptions.class));
    checkStoreInfo(storeInfo, Collections.singleton(addedId), (short) (lifeVersion + 2));

    // Undelete with same lifeVersion, should fail.
    info = new MessageInfo(addedId, UNDELETE_RECORD_SIZE, false, false, true, Utils.Infinite_Time, null,
        addedId.getAccountId(), addedId.getContainerId(), time.milliseconds(), (short) (lifeVersion + 2));
    try {
      store.undelete(info);
      fail("Should not succeed");
    } catch (StoreException e) {
      assertEquals(e.getErrorCode(), StoreErrorCodes.IDUndeleted);
      assertTrue(e instanceof IdUndeletedStoreException);
    }

    // Undelete with even larger lifeVersion, should succeed.
    info = new MessageInfo(addedId, UNDELETE_RECORD_SIZE, false, false, true, Utils.Infinite_Time, null,
        addedId.getAccountId(), addedId.getContainerId(), time.milliseconds(), (short) (lifeVersion + 4));
    store.undelete(info);
    storeInfo = store.get(Arrays.asList(addedId), EnumSet.noneOf(StoreGetOptions.class));
    checkStoreInfo(storeInfo, Collections.singleton(addedId), (short) (lifeVersion + 4));

    delete(addedId);

    addedId = put(1, PUT_RECORD_SIZE, Utils.Infinite_Time, Utils.getRandomShort(TestUtils.RANDOM),
        Utils.getRandomShort(TestUtils.RANDOM), lifeVersion).get(0);
    delete(addedId);
    undelete(addedId);
    storeInfo = store.get(Arrays.asList(addedId), EnumSet.noneOf(StoreGetOptions.class));
    checkStoreInfo(storeInfo, Collections.singleton(addedId), (short) (lifeVersion + 1));
    delete(addedId);
    undelete(addedId);
    storeInfo = store.get(Arrays.asList(addedId), EnumSet.noneOf(StoreGetOptions.class));
    checkStoreInfo(storeInfo, Collections.singleton(addedId), (short) (lifeVersion + 2));
  }
Tests when the lifeVersion for undelete is not -1.
@throws StoreException
ambry-store/src/test/java/com/github/ambry/store/BlobStoreTest.java