/**
   * In-progress LONG transactions written with DefaultSnapshotCodec will not have the type
   * serialized as part of the data. Since these transactions also contain a non-negative
   * expiration, we need to ensure we reset the type correctly when the snapshot is loaded.
   */
  @Test
  public void testV2ToTephraV3Compatibility() throws Exception {
    long now = System.currentTimeMillis();
    long nowWritePointer = now * TxConstants.MAX_TX_PER_MS;
    /*
     * Snapshot consisting of transactions at:
     */
    long tInvalid = nowWritePointer - 5; // t1 - invalid
    long readPtr = nowWritePointer - 4; // t2 - here and earlier committed
    long tLong = nowWritePointer - 3; // t3 - in-progress LONG
    long tCommitted = nowWritePointer - 2; // t4 - committed, changeset (r1, r2)
    long tShort =
        nowWritePointer - 1; // t5 - in-progress SHORT, canCommit called, changeset (r3, r4)

    TreeMap<Long, TransactionManager.InProgressTx> inProgress =
        Maps.newTreeMap(
            ImmutableSortedMap.of(
                tLong,
                    new TransactionManager.InProgressTx(
                        readPtr,
                        TransactionManager.getTxExpirationFromWritePointer(
                            tLong, TxConstants.Manager.DEFAULT_TX_LONG_TIMEOUT),
                        TransactionType.LONG),
                tShort,
                    new TransactionManager.InProgressTx(
                        readPtr, now + 1000, TransactionType.SHORT)));

    TransactionSnapshot snapshot =
        new TransactionSnapshot(
            now,
            readPtr,
            nowWritePointer,
            Lists.newArrayList(tInvalid), // invalid
            inProgress,
            ImmutableMap.<Long, Set<ChangeId>>of(
                tShort,
                Sets.newHashSet(
                    new ChangeId(new byte[] {'r', '3'}), new ChangeId(new byte[] {'r', '4'}))),
            ImmutableMap.<Long, Set<ChangeId>>of(
                tCommitted,
                Sets.newHashSet(
                    new ChangeId(new byte[] {'r', '1'}), new ChangeId(new byte[] {'r', '2'}))));

    Configuration conf1 = new Configuration();
    conf1.set(TxConstants.Persist.CFG_TX_SNAPHOT_CODEC_CLASSES, SnapshotCodecV2.class.getName());
    SnapshotCodecProvider provider1 = new SnapshotCodecProvider(conf1);

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    try {
      provider1.encode(out, snapshot);
    } finally {
      out.close();
    }

    TransactionSnapshot snapshot2 = provider1.decode(new ByteArrayInputStream(out.toByteArray()));
    assertEquals(snapshot.getReadPointer(), snapshot2.getReadPointer());
    assertEquals(snapshot.getWritePointer(), snapshot2.getWritePointer());
    assertEquals(snapshot.getInvalid(), snapshot2.getInvalid());
    // in-progress transactions will have missing types
    assertNotEquals(snapshot.getInProgress(), snapshot2.getInProgress());
    assertEquals(snapshot.getCommittingChangeSets(), snapshot2.getCommittingChangeSets());
    assertEquals(snapshot.getCommittedChangeSets(), snapshot2.getCommittedChangeSets());

    // after fixing in-progress, full snapshot should match
    Map<Long, TransactionManager.InProgressTx> fixedInProgress =
        TransactionManager.txnBackwardsCompatCheck(
            TxConstants.Manager.DEFAULT_TX_LONG_TIMEOUT, 10000L, snapshot2.getInProgress());
    assertEquals(snapshot.getInProgress(), fixedInProgress);
    assertEquals(snapshot, snapshot2);
  }