@Test
  public void verifyAssociationsPersistenceOnDissociation() throws Exception {

    // Unit test for #579
    // One target manager => write associations.
    String targetId_1 = this.mngr.createTarget("id: t1\nhandler: h");
    String targetId_2 = this.mngr.createTarget("id: t2\nhandler: h");
    TestApplication app = new TestApplication();
    String path = InstanceHelpers.computeInstancePath(app.getMySqlVm());

    this.mngr.associateTargetWith(targetId_1, app, null);
    this.mngr.associateTargetWith(targetId_2, app, path);

    Assert.assertEquals(targetId_1, this.mngr.findTargetId(app, null, true));
    Assert.assertEquals(targetId_2, this.mngr.findTargetId(app, path, true));

    // Create another manager and verify the associations
    ITargetsMngr newMngr = new TargetsMngrImpl(this.configurationMngr);
    Assert.assertEquals(targetId_1, newMngr.findTargetId(app, null, true));
    Assert.assertEquals(targetId_2, newMngr.findTargetId(app, path, true));

    // Now, dissociate target_2 and the root instance
    this.mngr.dissociateTargetFrom(app, path);
    Assert.assertEquals(targetId_1, this.mngr.findTargetId(app, null, true));
    Assert.assertNull(this.mngr.findTargetId(app, path, true));

    // Create another manager and verify the associations
    newMngr = new TargetsMngrImpl(this.configurationMngr);
    Assert.assertEquals(targetId_1, newMngr.findTargetId(app, null, true));
    Assert.assertNull(newMngr.findTargetId(app, path, true));
  }
  @Test
  public void testRestoreInstances_nonNullNonMatchingHandler() throws Exception {

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

    ITargetsMngr targetsMngr = Mockito.mock(ITargetsMngr.class);
    Mockito.when(
            targetsMngr.findRawTargetProperties(
                Mockito.any(Application.class), Mockito.anyString()))
        .thenReturn(new HashMap<String, String>(0));

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

    final TargetHandler targetHandlerArgument = Mockito.mock(TargetHandler.class);
    Mockito.when(targetHandlerArgument.getTargetId()).thenReturn("some target id");

    final TargetHandler targetHandler = Mockito.mock(TargetHandler.class);
    Mockito.when(targetHandler.getTargetId()).thenReturn("some other target id");

    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);

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

    // Try to restore instances
    mngr.restoreInstanceStates(ma, targetHandlerArgument);

    // The handler's ID did not match => no restoration and no use of other mocks
    Mockito.verify(targetsMngr, Mockito.only())
        .findRawTargetProperties(Mockito.eq(app), Mockito.anyString());
    Mockito.verify(targetHandler, Mockito.only()).getTargetId();
    Mockito.verify(targetHandlerArgument, Mockito.only()).getTargetId();

    Mockito.verifyZeroInteractions(messagingMngr);
    Mockito.verifyZeroInteractions(randomMngr);

    // No notification was sent since there was no change on Tomcat instances
    Mockito.verifyZeroInteractions(notificationMngr);
    Mockito.verifyZeroInteractions(targetConfigurator);
  }
  @Test
  public void testRestoreInstances_rightHandler_vmNotRunning() throws Exception {

    // Prepare stuff
    Map<String, String> targetProperties = new HashMap<>(0);

    INotificationMngr notificationMngr = Mockito.mock(INotificationMngr.class);
    IRandomMngr randomMngr = Mockito.mock(IRandomMngr.class);
    IMessagingMngr messagingMngr = Mockito.mock(IMessagingMngr.class);
    ITargetConfigurator targetConfigurator = Mockito.mock(ITargetConfigurator.class);

    ITargetsMngr targetsMngr = Mockito.mock(ITargetsMngr.class);
    Mockito.when(
            targetsMngr.findRawTargetProperties(
                Mockito.any(Application.class), Mockito.anyString()))
        .thenReturn(targetProperties);

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

    final TargetHandler targetHandlerArgument = Mockito.mock(TargetHandler.class);
    Mockito.when(targetHandlerArgument.getTargetId()).thenReturn("some target id");

    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 targetHandlerArgument;
              }
            });

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

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

    // Try to restore instances
    Assert.assertEquals(InstanceStatus.NOT_DEPLOYED, app.getMySqlVm().getStatus());
    app.getMySqlVm().setStatus(InstanceStatus.DEPLOYED_STARTED);

    Mockito.when(
            targetHandlerArgument.isMachineRunning(
                Mockito.any(TargetHandlerParameters.class), Mockito.eq("machine-id")))
        .thenReturn(false);

    mngr.restoreInstanceStates(ma, targetHandlerArgument);
    Assert.assertEquals(InstanceStatus.NOT_DEPLOYED, app.getMySqlVm().getStatus());

    // The handler's ID matched and the VM is NOT running => no message was sent.
    Mockito.verify(targetsMngr, Mockito.only())
        .findRawTargetProperties(Mockito.eq(app), Mockito.anyString());
    Mockito.verify(targetHandlerArgument, Mockito.times(1))
        .isMachineRunning(Mockito.any(TargetHandlerParameters.class), Mockito.eq("machine-id"));

    Mockito.verifyZeroInteractions(messagingMngr);
    Mockito.verifyZeroInteractions(randomMngr);
    Mockito.verifyZeroInteractions(targetConfigurator);

    // A notification was sent for the instance whose state changed
    Mockito.verify(notificationMngr)
        .instance(Mockito.any(Instance.class), Mockito.eq(app), Mockito.eq(EventType.CHANGED));
  }
  @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 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());
  }