package org.infinispan.server.test.l1;

import org.infinispan.arquillian.core.InfinispanResource;
import org.infinispan.arquillian.core.RemoteInfinispanServer;
import org.infinispan.arquillian.core.WithRunningServer;
import org.infinispan.server.test.client.memcached.MemcachedClient;
import org.jboss.arquillian.container.test.api.ContainerController;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
 * Tests for L1 caching.
 *
 * @author <a href="mailto:tsykora@redhat.com">Tomas Sykora</a>
 * @author <a href="mailto:wburns@redhat.com">William Burns</a>
 */
@RunWith(Arquillian.class)
@WithRunningServer({"l1-1", "l1-2"})
public class L1CachingTestCase {
   final String CONTAINER1 = "l1-1";
   final String CONTAINER2 = "l1-2";

   @InfinispanResource(CONTAINER1)
   RemoteInfinispanServer server1;

   @InfinispanResource(CONTAINER2)
   RemoteInfinispanServer server2;

   static final String ENCODING = "UTF-8";

   private MemcachedClient mc1;
   private MemcachedClient mc2;

   @Before
   public void setUp() throws Exception {
      mc1 = new MemcachedClient(ENCODING, server1.getMemcachedEndpoint().getInetAddress().getHostName(), server1
            .getMemcachedEndpoint().getPort(), 10000);
      mc2 = new MemcachedClient(ENCODING, server2.getMemcachedEndpoint().getInetAddress().getHostName(), server2
            .getMemcachedEndpoint().getPort(), 10000);
      mc1.delete("KeyA");
      mc2.delete("KeyA");
      mc1.delete("KeyB");
      mc2.delete("KeyB");
      mc2.delete("KeyBB");
      mc2.delete("KeyBB");
      mc1.delete("KeyC");
      mc2.delete("KeyC");
   }

   @After
   public void tearDown() throws Exception {
      if (mc1 != null) {
         mc1.close();
      }
      if (mc2 != null) {
         mc2.close();
      }
   }


   /*
   * Entries in L1 cache are stored directly into "main" cache. They are fetched from remote (distribution) node.
   * In TRACE we can see: "Doing a remote get for key KeyA" and "Caching remotely retrieved entry for key KeyA in L1"
   *
   * Number of hits is increased for cache on which is issued get. Hits on real-owner are still 0.
   * For caching L1 entries there is NO increase for number of stores. (Only for number of entries)
   */
   @Test
   public void testL1CachingEnabled() throws Exception {

      // NOTE
      // after https://issues.jboss.org/browse/ISPN-2120 (= 6.0.1 ER1)
      // I put to server1, then I issue get on server2 -> number of hits on server1 should be still 0
      // but number of hits on server2 should increase (entry was fetched from server1 to server2 for this get)
      mc1.set("KeyA", "A");
      mc1.set("KeyB", "B");
      mc1.set("KeyC", "C");

      assertTrue("Distribution of entries is wrong (at least unexpected).",
                 server1.getCacheManager("clustered").getCache("memcachedCache").getNumberOfEntries() > 0);
      assertTrue("Distribution of entries is wrong (at least unexpected).",
                 server2.getCacheManager("clustered").getCache("memcachedCache").getNumberOfEntries() > 0);

      assertEquals("More entries in caches than expected.", 3, server1.getCacheManager("clustered").getCache("memcachedCache").getNumberOfEntries() +
            server2.getCacheManager("clustered").getCache("memcachedCache").getNumberOfEntries());

      assertTrue("A".equals(mc2.get("KeyA")));
      assertTrue("B".equals(mc2.get("KeyB")));
      assertTrue("C".equals(mc2.get("KeyC")));

      assertEquals("Number of hits on server 1 is wrong.", 0, server1.getCacheManager("clustered").getCache("memcachedCache").getHits());
      assertEquals("Number of hits on server 2 is wrong.", 3, server2.getCacheManager("clustered").getCache("memcachedCache").getHits());

      assertEquals("Number of stores on server 1 is wrong.", 3, server1.getCacheManager("clustered").getCache("memcachedCache").getStores());
      assertEquals("Number of stores on server 2 is wrong.", 0, server2.getCacheManager("clustered").getCache("memcachedCache").getStores());

      // main condition (some entries are in the L1 cache - its copy is there)
      assertTrue("The are no entries in L1 cache! L1 seems to be disabled! Check TRACE [org.infinispan.factories.ComponentRegistry] output.",
                 server1.getCacheManager("clustered").getCache("memcachedCache").getNumberOfEntries() +
                       server2.getCacheManager("clustered").getCache("memcachedCache").getNumberOfEntries() > 3);

      // *****************************
      // do the same round for server1
      assertTrue("A".equals(mc1.get("KeyA")));
      assertTrue("B".equals(mc1.get("KeyB")));
      assertTrue("C".equals(mc1.get("KeyC")));

      assertEquals("Number of hits on server 1 is wrong.", 3, server1.getCacheManager("clustered").getCache("memcachedCache").getHits());
      assertEquals("Number of hits on server 2 is wrong.", 3, server2.getCacheManager("clustered").getCache("memcachedCache").getHits());

      // should be still the same as on the start
      assertEquals("Number of stores on server 1 is wrong.", 3, server1.getCacheManager("clustered").getCache("memcachedCache").getStores());
      assertEquals("Number of stores on server 2 is wrong.", 0, server2.getCacheManager("clustered").getCache("memcachedCache").getStores());

      // main condition (there should be server1 - 1 entry + 2 in L1, server2 - 2 entries + 1 in L1 = 3x2 = 6 num of entries JMX stat.
      assertEquals("The are no entries in L1 cache! L1 seems to be disabled! Check TRACE [org.infinispan.factories.ComponentRegistry] output.",
                   6, server1.getCacheManager("clustered").getCache("memcachedCache").getNumberOfEntries() +
            server2.getCacheManager("clustered").getCache("memcachedCache").getNumberOfEntries());
   }
}
