@Test
  public void testTargetsLocking_whenCreatingMachines_withExceptionInDeploy() throws Exception {

    // Prepare stuff
    INotificationMngr notificationMngr = Mockito.mock(INotificationMngr.class);
    ITargetsMngr targetsMngr = Mockito.mock(ITargetsMngr.class);
    IRandomMngr randomMngr = Mockito.mock(IRandomMngr.class);
    ITargetConfigurator targetConfigurator = Mockito.mock(ITargetConfigurator.class);

    IMessagingMngr messagingMngr = Mockito.mock(IMessagingMngr.class);
    Mockito.when(messagingMngr.getMessagingClient()).thenReturn(Mockito.mock(IDmClient.class));

    IConfigurationMngr configurationMngr = new ConfigurationMngrImpl();
    configurationMngr.setWorkingDirectory(this.folder.newFolder());

    final TargetHandler targetHandler = Mockito.mock(TargetHandler.class);
    Mockito.when(targetHandler.createMachine(Mockito.any(TargetHandlerParameters.class)))
        .thenThrow(new TargetException("for test"));

    IInstancesMngr mngr =
        new InstancesMngrImpl(
            messagingMngr, notificationMngr, targetsMngr, randomMngr, targetConfigurator);
    ((InstancesMngrImpl) mngr)
        .setTargetHandlerResolver(
            new TestTargetResolver() {
              @Override
              public TargetHandler findTargetHandler(Map<String, String> targetProperties)
                  throws TargetException {
                return targetHandler;
              }
            });

    TestApplication app = new TestApplication();
    ManagedApplication ma = new ManagedApplication(app);

    // We will try to create a machine. It will fail.
    // We must be sure the lock was acquired, and that it was released.
    final AtomicBoolean acquired = new AtomicBoolean(false);
    final AtomicBoolean released = new AtomicBoolean(false);

    Mockito.when(targetsMngr.lockAndGetTarget(app, app.getMySqlVm()))
        .thenAnswer(
            new Answer<Map<String, String>>() {

              @Override
              public Map<String, String> answer(InvocationOnMock invocation) throws Throwable {
                acquired.set(true);
                Map<String, String> result = new HashMap<>(0);
                return result;
              }
            });

    Mockito.doAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                released.set(true);
                return null;
              }
            })
        .when(targetsMngr)
        .unlockTarget(app, app.getMySqlVm());

    // Let's run assertions now
    Assert.assertFalse(acquired.get());
    Assert.assertFalse(released.get());
    Assert.assertEquals(InstanceStatus.NOT_DEPLOYED, app.getMySqlVm().getStatus());

    try {
      mngr.changeInstanceState(ma, app.getMySqlVm(), InstanceStatus.DEPLOYED_STARTED);
      Assert.fail("A target exception was expected.");

    } catch (Exception e) {
      // nothing
    }

    Assert.assertEquals(InstanceStatus.NOT_DEPLOYED, app.getMySqlVm().getStatus());
    Assert.assertTrue(acquired.get());
    Assert.assertTrue(released.get());

    // Since the handler failed, nothing was scheduled for post-configuration
    Mockito.verifyZeroInteractions(targetConfigurator);
  }
  @Test
  public void testNotificationsWhenUndeployingScopedInstances_changeInstanceState()
      throws Exception {

    // Prepare stuff
    final TestApplication app = new TestApplication();
    final Map<Instance, List<InstanceStatus>> instanceToStatusHistory = new HashMap<>();
    INotificationMngr notificationMngr =
        new NotificationMngrImpl() {
          @Override
          public void instance(Instance instance, Application application, EventType eventType) {

            Assert.assertEquals(EventType.CHANGED, eventType);
            Assert.assertEquals(app, application);

            List<InstanceStatus> status = instanceToStatusHistory.get(instance);
            if (status == null) {
              status = new ArrayList<>();
              instanceToStatusHistory.put(instance, status);
            }

            status.add(instance.getStatus());
          }
        };

    ITargetsMngr targetsMngr = Mockito.mock(ITargetsMngr.class);
    IRandomMngr randomMngr = Mockito.mock(IRandomMngr.class);
    ITargetConfigurator targetConfigurator = Mockito.mock(ITargetConfigurator.class);

    IMessagingMngr messagingMngr = Mockito.mock(IMessagingMngr.class);
    Mockito.when(messagingMngr.getMessagingClient()).thenReturn(Mockito.mock(IDmClient.class));

    IConfigurationMngr configurationMngr = new ConfigurationMngrImpl();
    configurationMngr.setWorkingDirectory(this.folder.newFolder());

    IInstancesMngr mngr =
        new InstancesMngrImpl(
            messagingMngr, notificationMngr, targetsMngr, randomMngr, targetConfigurator);
    ((InstancesMngrImpl) mngr).setTargetHandlerResolver(new TestTargetResolver());

    // Make one of our VM being fully deployed
    ManagedApplication ma = new ManagedApplication(app);
    app.getTomcatVm().setStatus(InstanceStatus.DEPLOYED_STARTED);
    app.getTomcat().setStatus(InstanceStatus.DEPLOYED_STARTED);
    app.getWar().setStatus(InstanceStatus.DEPLOYED_STARTED);

    // One scoped instance has a machine ID (considered as running somewhere)
    app.getTomcatVm().data.put(Instance.MACHINE_ID, "machine-id");

    // Stop everything
    mngr.changeInstanceState(ma, app.getTomcatVm(), InstanceStatus.NOT_DEPLOYED);

    // Check notifications
    Assert.assertEquals(3, instanceToStatusHistory.size());
    List<InstanceStatus> statusHistory = instanceToStatusHistory.get(app.getTomcatVm());
    Assert.assertNotNull(statusHistory);
    Assert.assertEquals(
        Arrays.asList(InstanceStatus.UNDEPLOYING, InstanceStatus.NOT_DEPLOYED), statusHistory);

    statusHistory = instanceToStatusHistory.get(app.getTomcat());
    Assert.assertNotNull(statusHistory);
    Assert.assertEquals(Arrays.asList(InstanceStatus.NOT_DEPLOYED), statusHistory);

    statusHistory = instanceToStatusHistory.get(app.getWar());
    Assert.assertNotNull(statusHistory);
    Assert.assertEquals(Arrays.asList(InstanceStatus.NOT_DEPLOYED), statusHistory);

    Mockito.verify(targetConfigurator, Mockito.only())
        .cancelCandidate(Mockito.any(TargetHandlerParameters.class), Mockito.eq(app.getTomcatVm()));
  }
  @Test
  public void testTargetsLocking_whenCreatingMachines_noException() throws Exception {

    // Prepare stuff
    INotificationMngr notificationMngr = Mockito.mock(INotificationMngr.class);
    ITargetsMngr targetsMngr = Mockito.mock(ITargetsMngr.class);
    IRandomMngr randomMngr = Mockito.mock(IRandomMngr.class);
    ITargetConfigurator targetConfigurator = Mockito.mock(ITargetConfigurator.class);

    IMessagingMngr messagingMngr = Mockito.mock(IMessagingMngr.class);
    Mockito.when(messagingMngr.getMessagingClient()).thenReturn(Mockito.mock(IDmClient.class));

    IConfigurationMngr configurationMngr = new ConfigurationMngrImpl();
    configurationMngr.setWorkingDirectory(this.folder.newFolder());

    IInstancesMngr mngr =
        new InstancesMngrImpl(
            messagingMngr, notificationMngr, targetsMngr, randomMngr, targetConfigurator);
    ((InstancesMngrImpl) mngr).setTargetHandlerResolver(new TestTargetResolver());

    TestApplication app = new TestApplication();
    ManagedApplication ma = new ManagedApplication(app);

    // We want to make sure target locking is correctly invoked by the instances manager.
    // So, we store requests to lock or unlock a target for a given instance.
    final Map<Instance, Integer> instancePathToLock = new HashMap<>();
    Mockito.when(targetsMngr.lockAndGetTarget(app, app.getMySqlVm()))
        .thenAnswer(
            new Answer<Map<String, String>>() {

              @Override
              public Map<String, String> answer(InvocationOnMock invocation) throws Throwable {

                Instance inst = invocation.getArgumentAt(1, Instance.class);
                Integer count = instancePathToLock.get(inst);
                count = count == null ? 1 : count + 1;
                instancePathToLock.put(inst, count);

                Map<String, String> result = new HashMap<>(0);
                return result;
              }
            });

    Mockito.doAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {

                Instance inst = invocation.getArgumentAt(1, Instance.class);
                Integer count = instancePathToLock.get(inst);
                count = count == null ? 0 : count - 1;
                if (count > 0) instancePathToLock.put(inst, count);
                else instancePathToLock.remove(inst);

                return null;
              }
            })
        .when(targetsMngr)
        .unlockTarget(app, app.getMySqlVm());

    // Let's run assertions now
    Assert.assertEquals(0, instancePathToLock.size());
    Assert.assertEquals(InstanceStatus.NOT_DEPLOYED, app.getMySqlVm().getStatus());
    mngr.changeInstanceState(ma, app.getMySqlVm(), InstanceStatus.DEPLOYED_STARTED);
    Assert.assertEquals(1, instancePathToLock.size());

    Integer lockCount = instancePathToLock.get(app.getMySqlVm());
    Assert.assertNotNull(lockCount);
    Assert.assertEquals(1, lockCount.intValue());
    Assert.assertEquals(InstanceStatus.DEPLOYING, app.getMySqlVm().getStatus());

    Mockito.verify(targetConfigurator)
        .reportCandidate(Mockito.any(TargetHandlerParameters.class), Mockito.eq(app.getMySqlVm()));

    // Release the machine
    mngr.changeInstanceState(ma, app.getMySqlVm(), InstanceStatus.NOT_DEPLOYED);
    Assert.assertEquals(InstanceStatus.NOT_DEPLOYED, app.getMySqlVm().getStatus());
    Assert.assertEquals(0, instancePathToLock.size());
  }