/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2010, Red Hat, Inc. and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.test.web.test;

import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.resetToStrict;
import static org.easymock.EasyMock.verify;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import junit.framework.TestCase;

import org.easymock.Capture;
import org.jboss.profileservice.spi.Profile;
import org.jboss.profileservice.spi.ProfileFactory;
import org.jboss.profileservice.spi.ProfileKey;
import org.jboss.profileservice.spi.ProfileService;
import org.jboss.profileservice.spi.metadata.ProfileMetaData;
import org.jboss.profileservice.spi.metadata.ProfileSourceMetaData;
import org.jboss.web.tomcat.service.ondemand.ContextDemandListener;
import org.jboss.web.tomcat.service.ondemand.OnDemandContextIntegrator;
import org.jboss.web.tomcat.service.ondemand.OnDemandContextProfileManager;

/**
 * Unit tests of {@link OnDemandContextProfileManager}
 *
 * @author Brian Stansberry
 * 
 * @version $Revision: 100854 $
 */
public class OnDemandContextProfileManagerUnitTestCase extends TestCase
{

   /**
    * Create a new OnDemandContextProfileManagerUnitTestCase.
    * 
    * @param name
    */
   public OnDemandContextProfileManagerUnitTestCase(String name)
   {
      super(name);
   }

   public void testGetProfileKey()
   {
      OnDemandContextProfileManager testee = new OnDemandContextProfileManager();
      try
      {
         testee.getProfileKey();
         fail("getProfileKey() must throw ISE if no profileName or contextName configured");
      }
      catch (IllegalStateException good) {}
      
      testee = new OnDemandContextProfileManager();
      testee.setContextName("");
      ProfileKey key = testee.getProfileKey();
      assertEquals(ProfileKey.DEFAULT, key.getDomain());
      assertEquals(ProfileKey.DEFAULT, key.getServer());
      assertEquals(OnDemandContextProfileManager.DEFAULT_ROOT_WAR_PROFILE_NAME, key.getName());
      
      testee = new OnDemandContextProfileManager();
      testee.setContextName("context");
      key = testee.getProfileKey();
      assertEquals(ProfileKey.DEFAULT, key.getDomain());
      assertEquals(ProfileKey.DEFAULT, key.getServer());
      assertEquals("context.war", key.getName());
      
      testee = new OnDemandContextProfileManager();
      testee.setProfileName("profile");
      key = testee.getProfileKey();
      assertEquals(ProfileKey.DEFAULT, key.getDomain());
      assertEquals(ProfileKey.DEFAULT, key.getServer());
      assertEquals("profile", key.getName());
      
      testee = new OnDemandContextProfileManager();
      testee.setProfileDomain("domain");
      testee.setProfileServer("server");
      testee.setProfileName("profile");
      key = testee.getProfileKey();
      assertEquals("domain", key.getDomain());
      assertEquals("server", key.getServer());
      assertEquals("profile", key.getName());
   }
   
   public void testRequiredInjections() throws Exception
   {
      OnDemandContextProfileManager testee = new OnDemandContextProfileManager();
      try
      {
         testee.start();
         fail("start() must throw ISE if injections are incomplete");
      }
      catch (IllegalStateException good) {}
      
      testee.setProfileFactory(createNiceMock(ProfileFactory.class));
      try
      {
         testee.start();
         fail("start() must throw ISE if injections are incomplete");
      }
      catch (IllegalStateException good) {}
      
      testee.setProfileService(createNiceMock(ProfileService.class));
      try
      {
         testee.start();
         fail("start() must throw ISE if injections are incomplete");
      }
      catch (IllegalStateException good) {}
      
      testee.setOnDemandContextIntegrator(createNiceMock(OnDemandContextIntegrator.class));
      try
      {
         testee.start();
         fail("start() must throw ISE if injections are incomplete");
      }
      catch (IllegalStateException good) {}
           
      testee.setServiceName("service");
      try
      {
         testee.start();
         fail("start() must throw ISE if injections are incomplete");
      }
      catch (IllegalStateException good) {}
           
      testee.setHostName("host");
      try
      {
         testee.start();
         fail("start() must throw ISE if injections are incomplete");
      }
      catch (IllegalStateException good) {}
           
      testee.setContextName("context");
      try
      {
         testee.start();
         fail("start() must throw ISE if injections are incomplete");
      }
      catch (IllegalStateException good) {}
      
      testee.setSingleURI(new URI("file:///tmp"));
      // Now everything needed is there
      testee.start();
      
   }
   
   public void testURIConfiguration() throws Exception
   {
      OnDemandContextProfileManager testee = new OnDemandContextProfileManager();
      List<URI> list = Arrays.asList(new URI("http://127.0.0.1"), new URI("file:///tmp"));
      testee.setURIList(list);
      assertEquals(list, testee.getURIList());
      assertNull(testee.getSingleURI());
      URI single = new URI("file://c:/");
      testee.setSingleURI(single);
      List<URI> singleList = Arrays.asList(single);
      assertEquals(singleList, testee.getURIList());
      assertEquals(single, testee.getSingleURI());
      testee.setURIList(list);
      assertEquals(list, testee.getURIList());
      assertNull(testee.getSingleURI());
      testee.setURIList(singleList);
      assertEquals(singleList, testee.getURIList());
      assertEquals(single, testee.getSingleURI());
      testee.setURIList(list);
      assertEquals(list, testee.getURIList());
      assertNull(testee.getSingleURI());
      
      try
      {
         testee.setURIList(null);
         fail("Setting null URI list did not throw IAE");
      }
      catch (IllegalArgumentException good) {}
      
      try
      {
         testee.setSingleURI(null);
         fail("Setting null single URI did not throw IAE");
      }
      catch (IllegalArgumentException good) {}
      
      List<URI> spottylist = Arrays.asList(new URI("http://127.0.0.1"), null, new URI("file:///tmp"));
      testee.setURIList(spottylist);
      assertEquals(list, testee.getURIList());
      assertNull(testee.getSingleURI());
      
      spottylist = Arrays.asList(null, new URI("http://127.0.0.1"), new URI("file:///tmp"));
      testee.setURIList(spottylist);
      assertEquals(list, testee.getURIList());
      assertNull(testee.getSingleURI());
      
      spottylist = Arrays.asList(new URI("http://127.0.0.1"), new URI("file:///tmp"), null);
      testee.setURIList(spottylist);
      assertEquals(list, testee.getURIList());
      assertNull(testee.getSingleURI());
   }
   
   public void testOnDemandEnabledStart() throws Exception
   {
      TesteeSet ts = new TesteeSet();
      Profile profile = createNiceMock(Profile.class);
      Capture<ProfileMetaData> capture = new Capture<ProfileMetaData>();
      expect(ts.profileFactory.createProfile(eq(ts.testee.getProfileKey()), capture(capture))).andReturn(profile);
      ts.profileService.registerProfile(profile);
      ContextDemandListener cdl = anyObject();
      ts.integrator.registerContextDemandListener(cdl);
      ts.integrator.registerOnDemandContext("service", "host", "/context");
      
      replay(ts.profileFactory);
      replay(ts.profileService);
      replay(ts.integrator);
      
      ts.testee.start();
      
      ProfileMetaData md = capture.getValue();
      assertNotNull(md);
      ProfileSourceMetaData psmd = md.getSource();
      assertNotNull(psmd);
      assertEquals(Arrays.asList(ts.testee.getSingleURI().toString()), psmd.getSources());
      
      verify(ts.profileFactory);
      verify(ts.profileService);
      verify(ts.integrator);
      
      assertFalse(ts.testee.isActivated());
   }
   
   public void testOnDemandDisabledStart() throws Exception
   {
      TesteeSet ts = new TesteeSet();
      ProfileKey profKey = ts.testee.getProfileKey();
      Profile profile = createNiceMock(Profile.class);
      Capture<ProfileMetaData> capture = new Capture<ProfileMetaData>();
      expect(ts.profileFactory.createProfile(eq(profKey), capture(capture))).andReturn(profile);
      
      resetToStrict(ts.profileService);
      ts.profileService.registerProfile(profile);
      Collection<ProfileKey> profiles = Collections.emptyList();
      expect(ts.profileService.getActiveProfileKeys()).andReturn(profiles);
      ts.profileService.activateProfile(profKey);
      replay(ts.profileFactory);
      replay(ts.profileService);
      replay(ts.integrator);
      
      ts.testee.setActivateOnDemand(false);
      ts.testee.start();
      
      ProfileMetaData md = capture.getValue();
      assertNotNull(md);
      ProfileSourceMetaData psmd = md.getSource();
      assertNotNull(psmd);
      assertEquals(Arrays.asList(ts.testee.getSingleURI().toString()), psmd.getSources());
      
      verify(ts.profileFactory);
      verify(ts.profileService);
      verify(ts.integrator);
      
      assertTrue(ts.testee.isActivated());
      
   }
   
   public void testStopAfterActivation() throws Exception
   {
      TesteeSet ts = new TesteeSet();
      ts.testee.start();
      
      assertFalse(ts.testee.isActivated());
      
      Collection<ProfileKey> profiles = Collections.emptyList();
      expect(ts.profileService.getActiveProfileKeys()).andReturn(profiles);
      replay(ts.profileService);
      
      ts.testee.activateProfile();
      
      assertTrue(ts.testee.isActivated());
      
      resetToStrict(ts.profileService);
      reset(ts.integrator);
      
      Collection<ProfileKey> keys = Collections.singletonList(ts.testee.getProfileKey());
      expect(ts.profileService.getActiveProfileKeys()).andReturn(keys);
      ts.profileService.deactivateProfile(ts.testee.getProfileKey());
      ts.profileService.unregisterProfile(ts.testee.getProfileKey());
      ContextDemandListener cdl = anyObject();
      ts.integrator.removeContextDemandListener(cdl);
      
      replay(ts.profileService);
      replay(ts.integrator);
      
      ts.testee.stop();
      
      verify(ts.integrator);
      verify(ts.profileService);
      
      assertFalse(ts.testee.isActivated());
   }
   
   public void testStopWithoutActivation() throws Exception
   {
      TesteeSet ts = new TesteeSet();
      ts.testee.start();
      
      assertFalse(ts.testee.isActivated());
      
      resetToStrict(ts.profileService);
      reset(ts.integrator);
      
      Collection<ProfileKey> keys = Collections.emptyList();
      expect(ts.profileService.getActiveProfileKeys()).andReturn(keys);
      ts.profileService.unregisterProfile(ts.testee.getProfileKey());
      ContextDemandListener cdl = anyObject();
      ts.integrator.removeContextDemandListener(cdl);
      
      replay(ts.profileService);
      replay(ts.integrator);
      
      ts.testee.stop();
      
      verify(ts.integrator);
      verify(ts.profileService);
   }
   
   public void testStopAfterFailedStart() throws Exception
   {
      // Case 1: it has a ProfileService and ProfileKey
      OnDemandContextProfileManager testee = new OnDemandContextProfileManager();
      ProfileService ps = createNiceMock(ProfileService.class);
      testee.setProfileService(ps);
      testee.setContextName("context");
      ProfileKey key = testee.getProfileKey();
      try
      {
         testee.start();
         fail("should have thrown ISE");
      }
      catch (IllegalStateException expected)
      {
         resetToStrict(ps);
         Collection<ProfileKey> profiles = Collections.emptyList();
         expect(ps.getActiveProfileKeys()).andReturn(profiles);
         ps.unregisterProfile(key);
         replay(ps);
         testee.stop();
         verify(ps);
      }
      
      // Case 2: it has nothing
      testee = new OnDemandContextProfileManager();
      try
      {
         testee.start();
         fail("should have thrown ISE");
      }
      catch (IllegalStateException expected)
      {
         testee.stop();
      }
   }
   
   public void testActivateProfile() throws Exception
   {
      TesteeSet ts = new TesteeSet();
      Profile profile = createNiceMock(Profile.class);
      ProfileMetaData md = anyObject();
      expect(ts.profileFactory.createProfile(eq(ts.testee.getProfileKey()), md)).andReturn(profile);
      
      replay(ts.profileFactory);
      replay(ts.profileService);
      replay(ts.integrator);
      
      ts.testee.start();
      
      assertFalse(ts.testee.isActivated());
      
      resetToStrict(ts.profileService);
      
      Collection<ProfileKey> profiles = Collections.emptyList();
      expect(ts.profileService.getActiveProfileKeys()).andReturn(profiles);
      ts.profileService.activateProfile(ts.testee.getProfileKey());
      ts.profileService.validateProfile(ts.testee.getProfileKey());
      
      replay(ts.profileService);
      
      ts.testee.activateProfile();
      
      verify(ts.profileService);
      
      assertTrue(ts.testee.isActivated());
   }
   
   public void testActivateAlreadyActiveProfile() throws Exception
   {
      TesteeSet ts = new TesteeSet();
      Profile profile = createNiceMock(Profile.class);
      ProfileMetaData md = anyObject();
      expect(ts.profileFactory.createProfile(eq(ts.testee.getProfileKey()), md)).andReturn(profile);
      
      replay(ts.profileFactory);
      replay(ts.profileService);
      replay(ts.integrator);
      
      ts.testee.start();
      
      assertFalse(ts.testee.isActivated());
      
      resetToStrict(ts.profileService);

      Collection<ProfileKey> profiles = Collections.singletonList(ts.testee.getProfileKey());
      expect(ts.profileService.getActiveProfileKeys()).andReturn(profiles);
      
      replay(ts.profileService);
      
      ts.testee.activateProfile();
      
      verify(ts.profileService);
      
      assertTrue(ts.testee.isActivated());
   }
   
   private static class TesteeSet
   {
      private final OnDemandContextProfileManager testee;
      private final ProfileFactory profileFactory;
      private final ProfileService profileService;
      private final OnDemandContextIntegrator integrator;
      
      private TesteeSet() throws IOException, URISyntaxException
      {
         testee = new OnDemandContextProfileManager();
         profileFactory = createNiceMock(ProfileFactory.class);
         testee.setProfileFactory(profileFactory);
         profileService = createNiceMock(ProfileService.class);
         testee.setProfileService(profileService);
         integrator = createNiceMock(OnDemandContextIntegrator.class);
         testee.setOnDemandContextIntegrator(integrator);
         testee.setServiceName("service");
         testee.setHostName("host");
         testee.setContextName("context");
         testee.setSingleURI(new URI("file:///tmp"));
      }
   }
}
