@Test(groups = {"dao"})
  public void testVoteAllocation() throws NoSuchUserException {
    // Set values convenient to our test
    final long voteCostToCreatePolicy = 20L;
    votingConfigManager.setVoteCostToCreatePolicy(voteCostToCreatePolicy);
    final byte voteWithdrawalPenaltyPercentage = (byte) 40;
    votingConfigManager.setVoteWithdrawalPenaltyPercentage(voteWithdrawalPenaltyPercentage);

    PolicyID policyID1 = new PolicyIDImpl();
    PolicyID policyID2 = new PolicyIDImpl();

    UserID userID1 = new UserIDImpl();
    CurrentUserVotesImpl dao = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID1);
    assertEmptyVoteAllocation(dao, userID1);
    assertEquals(dao.getVotesAllocated(policyID1), 0);
    assertEquals(dao.getVotesAllocated(policyID2), 0);

    // Setting to current value does nothing
    dao.setVotesAllocated(policyID1, 0);
    assertEquals(dao.getPolicyIDsVotedOn().size(), 0);
    assertEquals(dao.getPolicyVotes().size(), 0);
    assertFalse(dao.isDirty());

    // Save is a no op when not modified
    manager.save(dao);
    dao = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID1);
    assertEquals(dao.getPreviousVoteID(), ZERO_VOTE_RECORD_ID);
    long votesUnallocated = dao.getUnallocatedVotes();
    assertEquals(votesUnallocated, EXPECTED_INITIAL_VOTE_SALARY);
    assertEquals(dao.getPolicyVotes().size(), 0);

    // Allocate votes
    long policy1Votes = 10;
    dao.setVotesAllocated(policyID1, policy1Votes);
    votesUnallocated -= 10;

    long policy2Votes = 30;
    dao.setVotesAllocated(policyID2, policy2Votes);
    votesUnallocated -= 30;

    assertEquals(dao.getVotesAllocated(policyID1), policy1Votes);
    assertEquals(dao.getVotesAllocated(policyID2), policy2Votes);
    assertEquals(dao.getPolicyIDsVotedOn().size(), 2);
    assertEquals(dao.getUnallocatedVotes(), votesUnallocated);
    assertTrue(dao.isDirty());

    // Save changes and re-read
    manager.save(dao);
    dao = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID1);
    assertEquals(dao.getVotesAllocated(policyID1), policy1Votes);
    assertEquals(dao.getVotesAllocated(policyID2), policy2Votes);
    assertEquals(dao.getPolicyIDsVotedOn().size(), 2);
    assertEquals(dao.getUnallocatedVotes(), votesUnallocated);
    assertNull(dao.getCreatedPolicyID());
    assertEquals(dao.getUserID(), userID1);
    assertFalse(dao.isDirty());
    // New ID was written and will be the parent of this one if saved again
    assertNotEquals(dao.getPreviousVoteID(), ZERO_VOTE_RECORD_ID);

    // Other user has empty allocation
    UserID userID2 = new UserIDImpl();
    assertEmptyVoteAllocation(
        (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID2), userID2);

    // PolicyDetails creation
    PolicyID policyID3 = new PolicyIDImpl();
    dao.recordPolicyCreation(policyID3);
    long policy3Votes = voteCostToCreatePolicy;
    votesUnallocated -= voteCostToCreatePolicy;
    assertEquals(dao.getUnallocatedVotes(), votesUnallocated);
    assertEquals(dao.getCreatedPolicyID(), policyID3);
    assertEquals(dao.getVotesAllocated(policyID3), policy3Votes);
    assertEquals(dao.getPolicyIDsVotedOn().size(), 3);
    assertEquals(dao.getPolicyVotes().size(), 3);
    assertTrue(dao.isDirty());
    manager.save(dao);

    // Trying to allocate more votes than we have will fail
    dao = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID1);
    assertEquals(dao.getUnallocatedVotes(), votesUnallocated);
    // Allowed
    assertEquals(dao.getVotesAllocated(policyID2), policy2Votes);
    long policy2MaxVotes = policy2Votes + votesUnallocated;
    dao.setVotesAllocated(policyID2, policy2MaxVotes);
    assertEquals(dao.getVotesAllocated(policyID2), policy2MaxVotes);
    assertTrue(dao.isDirty());

    dao = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID1);
    assertEquals(dao.getUnallocatedVotes(), votesUnallocated);
    // Not allowed
    assertEquals(dao.getVotesAllocated(policyID2), policy2Votes);
    try {
      dao.setVotesAllocated(policyID2, policy2MaxVotes + 1);
      fail();
    } catch (InsufficientVotesException expected) {
    }
    // Shoud be unchanged
    assertEquals(dao.getVotesAllocated(policyID2), policy2Votes);
    assertFalse(dao.isDirty());

    // Vote withdrawals and associated penalty
    policy2Votes -= 10;
    dao.setVotesAllocated(policyID2, policy2Votes);
    votesUnallocated += (10 * (1.0 - (voteWithdrawalPenaltyPercentage / 100.0)));
    assertEquals(dao.getUnallocatedVotes(), votesUnallocated);
    assertEquals(dao.getVotesAllocated(policyID2), policy2Votes);
    assertTrue(dao.isDirty());

    // Withdrawal of one vote is entirely consumed by the penalty due to rounding
    assertEquals(dao.getVotesAllocated(policyID1), policy1Votes);
    policy1Votes -= 1;
    dao.setVotesAllocated(policyID1, policy1Votes);
    assertEquals(dao.getUnallocatedVotes(), votesUnallocated);
    assertEquals(dao.getVotesAllocated(policyID1), policy1Votes);
    manager.save(dao);

    dao = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID1);
    assertEquals(dao.getUnallocatedVotes(), votesUnallocated);
    assertEquals(dao.getVotesAllocated(policyID1), policy1Votes);
    assertEquals(dao.getVotesAllocated(policyID2), policy2Votes);
    assertEquals(dao.getVotesAllocated(policyID3), policy3Votes);
    assertEquals(dao.getPolicyIDsVotedOn().size(), 3);

    // Prospective allocations don't change anything
    assertFalse(dao.isDirty());
    assertEquals(dao.getUnallocatedVoteBalanceChangeIfWeAllocatedThis(policyID3, policy3Votes), 0);
    assertEquals(
        dao.getUnallocatedVoteBalanceChangeIfWeAllocatedThis(policyID3, policy3Votes + 10), -10);
    assertEquals(
        dao.getUnallocatedVoteBalanceChangeIfWeAllocatedThis(policyID3, policy3Votes - 10),
        (long) (10 * (1.0 - (voteWithdrawalPenaltyPercentage / 100.0))));
    assertEquals(
        dao.getUnallocatedVoteBalanceChangeIfWeAllocatedThis(
            policyID3, policy3Votes + votesUnallocated),
        -votesUnallocated);
    try {
      dao.getUnallocatedVoteBalanceChangeIfWeAllocatedThis(
          policyID3, policy3Votes + votesUnallocated + 1);
      fail();
    } catch (InsufficientVotesException expected) {
    }
    assertEquals(dao.getVotesAllocated(policyID3), policy3Votes);
    assertFalse(dao.isDirty());

    // Allocations can't be negative
    try {
      dao.getUnallocatedVoteBalanceChangeIfWeAllocatedThis(policyID3, -1);
      fail();
    } catch (IllegalArgumentException expected) {
    }
    try {
      dao.setVotesAllocated(policyID3, -1);
      fail();
    } catch (IllegalArgumentException expected) {
    }
  }
  @Test(groups = {"dao"})
  public void testCollisionResolution() throws NoSuchUserException, InterruptedException {
    // If test sometimes fails on slow machines, try increasing this value.
    final long voteFinalizeDelaySeconds = 2L;
    votingConfigManager.setVoteFinalizeDelaySeconds(voteFinalizeDelaySeconds);

    PolicyID policyID1 = new PolicyIDImpl();
    PolicyID policyID2 = new PolicyIDImpl();

    UserID userID = new UserIDImpl();
    CurrentUserVotesImpl sharedBranch =
        (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID);
    assertEmptyVoteAllocation(sharedBranch, userID);
    final long initialUnallocated = sharedBranch.getUnallocatedVotes();
    long branch1Unallocated = initialUnallocated;

    // Assign and save 2 vote records to 1
    sharedBranch.setVotesAllocated(policyID1, 2);
    sharedBranch.setVotesAllocated(policyID2, 1);
    branch1Unallocated -= (2 + 1);
    manager.save(sharedBranch);
    sharedBranch = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID);
    assertEquals(sharedBranch.getUnallocatedVotes(), initialUnallocated - (2 + 1));
    assertEquals(sharedBranch.getVotesAllocated(policyID1), 2);
    assertEquals(sharedBranch.getVotesAllocated(policyID2), 1);

    sharedBranch.setVotesAllocated(policyID1, 4);
    sharedBranch.setVotesAllocated(policyID2, 2);
    branch1Unallocated -= (2 + 1);
    manager.save(sharedBranch);
    // This will be the last common parent after which the two branches diverge
    sharedBranch = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID);
    assertEquals(sharedBranch.getUnallocatedVotes(), initialUnallocated - (4 + 2));
    assertEquals(sharedBranch.getVotesAllocated(policyID1), 4);
    assertEquals(sharedBranch.getVotesAllocated(policyID2), 2);

    // Use split point as basis for 2nd "branch" of vote saves
    CurrentUserVotesImpl branch1 =
        (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID);
    branch1.setVotesAllocated(policyID1, 16);
    branch1.setVotesAllocated(policyID2, 13);
    manager.save(branch1);
    branch1 = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID);
    assertEquals(branch1.getUnallocatedVotes(), initialUnallocated - (16 + 13));
    assertEquals(branch1.getVotesAllocated(policyID1), 16);
    assertEquals(branch1.getVotesAllocated(policyID2), 13);

    branch1.setVotesAllocated(policyID1, 18);
    branch1.setVotesAllocated(policyID2, 14);
    manager.save(branch1);
    branch1 = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID);
    assertEquals(branch1.getUnallocatedVotes(), initialUnallocated - (18 + 14));
    assertEquals(branch1.getVotesAllocated(policyID1), 18);
    assertEquals(branch1.getVotesAllocated(policyID2), 14);

    // Save on branch2 from the common ancestor, confirm retrievable
    CurrentUserVotesImpl branch2 = sharedBranch;
    branch2.setVotesAllocated(policyID1, 6);
    branch2.setVotesAllocated(policyID2, 3);
    manager.save(branch2);
    branch2 = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID);
    assertEquals(branch2.getUnallocatedVotes(), initialUnallocated - (6 + 3));
    assertEquals(branch2.getVotesAllocated(policyID1), 6);
    assertEquals(branch2.getVotesAllocated(policyID2), 3);

    branch2.setVotesAllocated(policyID1, 8);
    branch2.setVotesAllocated(policyID2, 4);
    manager.save(branch2);
    final long branch2LastSavedMillis = System.currentTimeMillis();
    branch2 = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID);
    assertEquals(branch2.getUnallocatedVotes(), initialUnallocated - (8 + 4));
    assertEquals(branch2.getVotesAllocated(policyID1), 8);
    assertEquals(branch2.getVotesAllocated(policyID2), 4);

    // Try saving on branch1 head again; retrieve; branch1 then wins because it's newer; even though
    // branch1 lost
    // the conflict resolution before, it hasn't been expired since we didn't read
    // voteFinalizeDelaySeconds;
    // so now it wins again.
    branch1.setVotesAllocated(policyID1, 20);
    branch1.setVotesAllocated(policyID2, 15);
    manager.save(branch1);
    CurrentUserVotesImpl conflicted =
        (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID);
    assertEquals(conflicted.getUnallocatedVotes(), initialUnallocated - (20 + 15));
    assertEquals(conflicted.getVotesAllocated(policyID1), 20);
    assertEquals(conflicted.getVotesAllocated(policyID2), 15);

    // Wait until voteFinalizeDelaySeconds has passed after last write on branch2
    while (true) {
      long now = System.currentTimeMillis();
      long earlyBy = (branch2LastSavedMillis + (voteFinalizeDelaySeconds * 1000L)) - now;
      if (earlyBy > 0) {
        Thread.sleep(earlyBy);
      } else {
        break;
      }
    }

    // Re-retrieve, should see branch1 still; the re-retrieve will expire the branch2 stuff because
    // it's old
    conflicted = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID);
    assertEquals(conflicted.getUnallocatedVotes(), initialUnallocated - (20 + 15));
    assertEquals(conflicted.getVotesAllocated(policyID1), 20);
    assertEquals(conflicted.getVotesAllocated(policyID2), 15);

    // Write to branch2, knowing that parent is already expired
    // Branch2 already lost the conflict resolution when we retrieved branch1, and was deleted
    // because it was old enough,
    // hence the new write has missing parents and is discarded.
    branch2.setVotesAllocated(policyID1, 10);
    branch2.setVotesAllocated(policyID2, 5);
    manager.save(branch2);

    // Assert that the retrievable values are still the ones from branch1
    conflicted = (CurrentUserVotesImpl) manager.getCurrentUserVoteAllocation(userID);
    assertEquals(conflicted.getUnallocatedVotes(), initialUnallocated - (20 + 15));
    assertEquals(conflicted.getVotesAllocated(policyID1), 20);
    assertEquals(conflicted.getVotesAllocated(policyID2), 15);
  }