@Test
  public void undeleteCrossLogSegmentTest_UndeleteAsFinal() throws Exception {
    assumeTrue(withUndelete);
    refreshState(false, false, false);
    int numPuts = (int) Math.floorDiv(state.log.getSegmentCapacity() - LogSegment.HEADER_SIZE,
        PUT_RECORD_SIZE + (DELETE_RECORD_SIZE) / 2) - 1;
    List<IndexEntry> entries = state.addPutEntries(numPuts, PUT_RECORD_SIZE, Utils.Infinite_Time);
    for (IndexEntry entry : entries) {
      state.addDeleteEntry((MockId) entry.getKey());
    }
    writeDataToMeetRequiredSegmentCount(2, null);
    for (IndexEntry entry : entries) {
      state.addUndeleteEntry((MockId) entry.getKey());
    }
    writeDataToMeetRequiredSegmentCount(4, null);
    state.reloadIndex(true, false);

    // Here we have several PUTs in the first log segments and half of the DELETEs in the first log segment
    // and the other half in the second. Then we have all the UNDELETEs in the third log segment.
    List<LogSegmentName> segmentsUnderCompaction = getLogSegments(0, state.index.getLogSegmentCount() - 1);
    compactAndVerify(segmentsUnderCompaction, state.time.milliseconds(), true);

    // For all the keys, in entries, there should only have two index value left, one Put, one Undelete.
    for (IndexEntry entry : entries) {
      List<IndexValue> values = state.index.findAllIndexValuesForKey(entry.getKey(), null);
      assertNotNull(values);
      assertEquals(2, values.size());
      IndexValue undelete = values.get(0);
      IndexValue put = values.get(1);
      assertTrue(undelete.isUndelete());
      assertFalse(undelete.isDelete());
      assertEquals((short) 1, undelete.getLifeVersion());
      IndexValue expected = entry.getValue();
      assertEquals(expected.isPut(), put.isPut());
      assertEquals(expected.getLifeVersion(), put.getLifeVersion());
      assertEquals(expected.getAccountId(), put.getAccountId());
      assertEquals(expected.getContainerId(), put.getContainerId());
      assertEquals(expected.getSize(), put.getSize());
      assertEquals(expected.getExpiresAtMs(), put.getExpiresAtMs());
      assertEquals(expected.getOperationTimeInMs(), put.getOperationTimeInMs());
    }
  }
Tests when the PUT DELETE and UNDELETE are located in different log segments and the undelete is the final state.
@throws Exception
ambry-store/src/test/java/com/github/ambry/store/BlobStoreCompactorTest.java