package org.jboss.cache.commands.write;

import static org.easymock.EasyMock.createStrictControl;
import static org.easymock.EasyMock.expect;
import org.easymock.IMocksControl;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.DataContainer;
import org.jboss.cache.commands.read.AbstractDataCommandTest;
import org.jboss.cache.mock.MockNodesFixture;
import org.jboss.cache.notifications.Notifier;
import org.jboss.cache.optimistic.DataVersion;
import org.jboss.cache.optimistic.DefaultDataVersion;
import org.testng.annotations.Test;

import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.util.Collections;

/**
 * tester class for {@link OptimisticInvalidateCommand}.
 *
 * @author Mircea.Markus@jboss.com
 * @since 2.2
 */
@Test(groups = "unit")
public class OptimisticInvalidateCommandTest extends AbstractDataCommandTest
{
   DataVersion dataVersion;
   OptimisticInvalidateCommand command;
   IMocksControl control;

   Notifier notifier;
   TransactionManager tmMock;

   MockNodesFixture nodes;
   CacheSPI spiMock;

   protected void moreSetup()
   {
      control = createStrictControl();
      notifier = control.createMock(Notifier.class);
      container = control.createMock(DataContainer.class);
      tmMock = control.createMock(TransactionManager.class);
      spiMock = control.createMock(CacheSPI.class);
      nodes = new MockNodesFixture();

      command = new OptimisticInvalidateCommand(testFqn);
      dataVersion = new DefaultDataVersion(10);
      command.setDataVersion(dataVersion);
      command.initialize(spiMock, container, notifier);
      command.initialize(tmMock);
   }

   public void testWithExistingNode()
   {
      nodes.adfNode.put("key", "value");
      nodes.adfNode.setDataLoaded(true);
      expect(spiMock.getNode(testFqn)).andReturn(nodes.adfNode);
      expect(container.peekVersioned(testFqn, dataVersion)).andReturn(nodes.adfNode);
      notifier.notifyNodeEvicted(testFqn, true, ctx);
      notifier.notifyNodeEvicted(testFqn, false, ctx);
      expect(container.peek(testFqn, false, true)).andReturn(nodes.adfNode);

      control.replay();
      assert null == command.perform(ctx);
      assert nodes.adfNode.getData().isEmpty();
      assert !nodes.adfNode.isDataLoaded();
      assert !nodes.adfNode.isValid();
      assert nodes.adfNode.getVersion().equals(dataVersion);

      control.verify();
   }

   public void testWithExistingNodeInvalidVersion()
   {
      nodes.adfNode.put("key", "value");
      nodes.adfNode.setDataLoaded(true);
      expect(spiMock.getNode(testFqn)).andReturn(nodes.adfNode);
      expect(container.peekVersioned(testFqn, dataVersion)).andThrow(new RuntimeException());
      control.replay();

      try
      {
         command.perform(ctx);
         assert false : "exception expected";
      }
      catch (Exception e)
      {
         //expected as there is a version mismatch
      }
      assert !nodes.adfNode.getData().isEmpty();
      assert nodes.adfNode.isDataLoaded();
      assert nodes.adfNode.isValid();
      assert !dataVersion.equals(nodes.adfNode.getVersion());

      control.verify();
   }

   public void testExistingTumbstone()
   {
      nodes.adfNode.setValid(false, true);
      expect(spiMock.getNode(testFqn)).andReturn(null);
      expect(container.peek(testFqn, false, true)).andReturn(nodes.adfNode);
      expect(container.peekVersioned(testFqn, dataVersion)).andReturn(nodes.adfNode);
      notifier.notifyNodeEvicted(testFqn, true, ctx);
      notifier.notifyNodeEvicted(testFqn, false, ctx);
      expect(container.peek(testFqn, false, true)).andReturn(nodes.adfNode);

      control.replay();
      assert null == command.perform(ctx);
      assert nodes.adfNode.getData().isEmpty();
      assert !nodes.adfNode.isDataLoaded();
      assert !nodes.adfNode.isValid();
      assert nodes.adfNode.getVersion().equals(dataVersion);
      control.verify();
   }

   public void testCreateTumbstone() throws Exception
   {
      Transaction tx = control.createMock(Transaction.class);
      expect(tmMock.suspend()).andReturn(tx);
      spiMock.put(testFqn, Collections.emptyMap());
      tmMock.resume(tx);

      control.replay();
      command.createTombstone(ctx);
      control.verify();
   }

   public void testCreateTumbstoneNoTx() throws Exception
   {
      expect(tmMock.suspend()).andReturn(null);
      spiMock.put(testFqn, Collections.EMPTY_MAP);

      control.replay();
      command.createTombstone(ctx);
      control.verify();
   }
}
