FileSpan addDeleteEntry(MockId idToDelete, MessageInfo info, short lifeVersion) throws StoreException {
    IndexValue value = getExpectedValue(idToDelete, false);
    boolean hasLifeVersion = IndexValue.hasLifeVersion(lifeVersion);
    //check if we should reject this delete entry
    if (value == null && !allKeys.containsKey(idToDelete) && info == null) {
      throw new IllegalArgumentException(idToDelete + " does not exist in the index");
    } else if (value != null && value.isDelete()) {
      if (lifeVersion == MessageInfo.LIFE_VERSION_FROM_FRONTEND) {
        throw new IllegalArgumentException(idToDelete + " is already deleted");
      } else {
        if (value.getLifeVersion() >= lifeVersion) {
          throw new IllegalArgumentException(idToDelete + " lifeVersion Conflict");
        }
      }
    }

    //add delete to the log
    byte[] dataWritten = appendToLog(CuratedLogIndexState.DELETE_RECORD_SIZE);
    Offset endOffsetOfPrevMsg = index.getCurrentEndOffset();
    FileSpan fileSpan = log.getFileSpanForMessage(endOffsetOfPrevMsg, CuratedLogIndexState.DELETE_RECORD_SIZE);
    Offset startOffset = fileSpan.getStartOffset();
    Offset indexSegmentStartOffset = generateReferenceIndexSegmentStartOffset(startOffset);
    if (!referenceIndex.containsKey(indexSegmentStartOffset)) {
      // rollover will occur
      advanceTime(DELAY_BETWEEN_LAST_MODIFIED_TIMES_MS);
      referenceIndex.put(indexSegmentStartOffset, new TreeMap<>());
    }

    //add delete to the index
    boolean forceDeleteEntry = false;
    IndexValue newValue;
    if (value != null) {
      short delLifeVersion = hasLifeVersion ? lifeVersion : value.getLifeVersion();
      newValue = new IndexValue(value.getSize(), value.getOffset(), value.getFlags(), value.getExpiresAtMs(),
          info == null ? time.milliseconds() : info.getOperationTimeMs(), value.getAccountId(), value.getContainerId(),
          delLifeVersion);
      newValue.setNewOffset(startOffset);
      newValue.setNewSize(CuratedLogIndexState.DELETE_RECORD_SIZE);
      if (!value.isPut()) {
        newValue.clearOriginalMessageOffset();
      }
    } else if (allKeys.containsKey(idToDelete)) {
      // this is because a ttl update record was forcibly added
      value = allKeys.get(idToDelete).last();
      short delLifeVersion = hasLifeVersion ? lifeVersion : value.getLifeVersion();
      newValue =
          new IndexValue(CuratedLogIndexState.DELETE_RECORD_SIZE, startOffset, value.getFlags(), value.getExpiresAtMs(),
              time.milliseconds(), value.getAccountId(), value.getContainerId(), delLifeVersion);
      newValue.clearOriginalMessageOffset();
      forceDeleteEntry = true;
    } else {
      // Force to add the delete entry to the log to mimic the situation when compaction happens to remove the put.
      newValue = new IndexValue(CuratedLogIndexState.DELETE_RECORD_SIZE, startOffset, IndexValue.FLAGS_DEFAULT_VALUE,
          info.getExpirationTimeInMs(), info.getOperationTimeMs(), info.getAccountId(), info.getContainerId(),
          info.getLifeVersion());
      newValue.clearOriginalMessageOffset();
      if (info.isTtlUpdated()) {
        newValue.setFlag(IndexValue.Flags.Ttl_Update_Index);
      }
      forceDeleteEntry = true;
    }
    newValue.setFlag(IndexValue.Flags.Delete_Index);
    newValue.clearFlag(IndexValue.Flags.Undelete_Index);
    if (forceDeleteEntry) {
      index.addToIndex(new IndexEntry(idToDelete, newValue), fileSpan);
    } else {
      index.markAsDeleted(idToDelete, fileSpan, null, newValue.getOperationTimeInMs(), newValue.getLifeVersion());
    }
    markAsDeleted(idToDelete);
    logOrder.put(startOffset, new Pair<>(idToDelete, new LogEntry(dataWritten, newValue)));
    allKeys.computeIfAbsent(idToDelete, k -> new TreeSet<>()).add(newValue);
    referenceIndex.get(indexSegmentStartOffset).computeIfAbsent(idToDelete, k -> new TreeSet<>()).add(newValue);
    lastModifiedTimesInSecs.put(indexSegmentStartOffset, newValue.getOperationTimeInMs() / Time.MsPerSec);
    endOffsetOfPrevMsg = fileSpan.getEndOffset();
    assertEquals("End Offset of index not as expected", endOffsetOfPrevMsg, index.getCurrentEndOffset());
    assertEquals("Journal's last offset not as expected", startOffset, index.journal.getLastOffset());
    return fileSpan;
  }
Adds a delete entry in the index (real and reference) for {@code idToDelete}.
@param idToDelete the id to be deleted.
@param info the {@link MessageInfo} to use in case of recovery to fetch accountId, containerId and operationTime.
@return the {@link FileSpan} of the added entries.
@throws StoreException
ambry-store/src/test/java/com/github/ambry/store/CuratedLogIndexState.java