org.ligoj.app.plugin.vm.aws.VmAwsSnapshotResourceTest.java Source code

Java tutorial

Introduction

Here is the source code for org.ligoj.app.plugin.vm.aws.VmAwsSnapshotResourceTest.java

Source

/*
 * Licensed under MIT (https://github.com/ligoj/ligoj/blob/master/LICENSE)
 */
package org.ligoj.app.plugin.vm.aws;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

import javax.transaction.Transactional;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.ligoj.app.AbstractServerTest;
import org.ligoj.app.dao.SubscriptionRepository;
import org.ligoj.app.iam.IUserRepository;
import org.ligoj.app.iam.IamConfiguration;
import org.ligoj.app.iam.IamProvider;
import org.ligoj.app.model.Node;
import org.ligoj.app.model.Parameter;
import org.ligoj.app.model.ParameterValue;
import org.ligoj.app.model.Project;
import org.ligoj.app.model.Subscription;
import org.ligoj.app.plugin.vm.model.SnapshotOperation;
import org.ligoj.app.plugin.vm.model.VmSchedule;
import org.ligoj.app.plugin.vm.model.VmSnapshotStatus;
import org.ligoj.app.plugin.vm.snapshot.Snapshot;
import org.ligoj.app.plugin.vm.snapshot.VmSnapshotResource;
import org.ligoj.app.resource.subscription.SubscriptionResource;
import org.ligoj.bootstrap.core.DateUtils;
import org.ligoj.bootstrap.core.resource.BusinessException;
import org.mockito.ArgumentMatcher;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.xml.sax.SAXException;

/**
 * Test class of {@link VmAwsSnapshotResource}
 */
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "classpath:/META-INF/spring/application-context-test.xml")
@Rollback
@Transactional
public class VmAwsSnapshotResourceTest extends AbstractServerTest {

    private VmAwsSnapshotResource resource;

    @Autowired
    private SubscriptionResource subscriptionResource;

    @Autowired
    private SubscriptionRepository subscriptionRepository;

    protected int subscription;

    @BeforeEach
    public void prepareData() throws Exception {
        // Only with Spring context
        persistSystemEntities();
        persistEntities("csv", new Class[] { Node.class, Parameter.class, Project.class, Subscription.class,
                ParameterValue.class, VmSchedule.class }, StandardCharsets.UTF_8.name());
        this.subscription = getSubscription("gStack");

        // Invalidate vCloud cache
        cacheManager.getCache("curl-tokens").clear();

        resource = new VmAwsSnapshotResource();
        applicationContext.getAutowireCapableBeanFactory().autowireBean(resource);
        resource.snapshotResource = Mockito.mock(VmSnapshotResource.class);
        resource.resource = Mockito.mock(VmAwsPluginResource.class);
    }

    @Test
    public void findAllByNameOrId() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all.xml");

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "");
        Assertions.assertEquals(2, snapshosts.size());
        final Snapshot snapshot = checkAmiPart(snapshosts.get(0));
        Assertions.assertNotNull(snapshot.getDate());
        Assertions.assertNotNull(snapshot.getAuthor().getFirstName());
    }

    /**
     * Last snapshot task is locally and remotely finished without error.
     */
    @Test
    public void findAllByNameOrIdTaskFinished() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all.xml");
        final VmSnapshotStatus status = new VmSnapshotStatus();
        status.setFinishedRemote(true);
        status.setEnd(new Date());
        status.setAuthor("ligoj-admin");
        status.setSnapshotInternalId("ami-00000001");
        status.setLocked(subscriptionRepository.findOneExpected(subscription));
        Mockito.when(resource.snapshotResource.getTask(subscription)).thenReturn(status);

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "");
        Assertions.assertEquals(2, snapshosts.size());
        checkAmiPart(snapshosts.get(0));
    }

    /**
     * Last snapshot task is locally and remotely finished without error.
     */
    @Test
    public void findAllByNameOrIdTaskFailed() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all.xml");
        final VmSnapshotStatus status = new VmSnapshotStatus();
        status.setFinishedRemote(true);
        status.setEnd(new Date());
        status.setFailed(true);
        status.setAuthor("ligoj-admin");
        status.setSnapshotInternalId("ami-00000004");
        status.setStatusText("some-error");
        status.setLocked(subscriptionRepository.findOneExpected(subscription));
        Mockito.when(resource.snapshotResource.getTask(subscription)).thenReturn(status);

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "");
        Assertions.assertEquals(3, snapshosts.size());
        checkAmiPart(snapshosts.get(1));

        // Failed task, unlisted AMI, failed and not created AMI
        Assertions.assertEquals("ligoj-admin", snapshosts.get(0).getAuthor().getId());
        Assertions.assertEquals("ami-00000004", snapshosts.get(0).getId());
        Assertions.assertFalse(snapshosts.get(0).isPending());
        Assertions.assertFalse(snapshosts.get(0).isAvailable());
        Assertions.assertEquals("some-error", snapshosts.get(0).getStatusText());
    }

    /**
     * Last snapshot task is locally finished but the AMI is not yet listed and not found by direct lookup.
     */
    @Test
    public void findAllByNameOrIdTaskFinishedLocallyNoAws() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all.xml");
        final VmSnapshotStatus status = new VmSnapshotStatus();
        status.setEnd(new Date());
        status.setAuthor("ligoj-admin2");
        status.setSnapshotInternalId("ami-00000004");
        status.setLocked(subscriptionRepository.findOneExpected(subscription));
        Mockito.when(resource.snapshotResource.getTask(subscription)).thenReturn(status);

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "");
        Assertions.assertEquals(3, snapshosts.size());
        checkAmiPart(snapshosts.get(1));

        // Unfinished task, unlisted AMI and not created AMI
        Assertions.assertEquals("ligoj-admin2", snapshosts.get(0).getAuthor().getId());
        Assertions.assertEquals("ami-00000004", snapshosts.get(0).getId());
        Assertions.assertTrue(snapshosts.get(0).isPending());
        Assertions.assertFalse(snapshosts.get(0).isAvailable());
        Assertions.assertEquals("not-found", snapshosts.get(0).getStatusText());
    }

    /**
     * Last snapshot task is locally finished but the AMI was not yet listed, is found by direct lookup and now listed.
     */
    @Test
    public void findAllByNameOrIdTaskFinishedJustListed() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all-with-00000004.xml");
        final VmSnapshotStatus status = new VmSnapshotStatus();
        status.setEnd(new Date());
        status.setAuthor("ligoj-admin2");
        status.setSnapshotInternalId("ami-00000004");
        status.setLocked(subscriptionRepository.findOneExpected(subscription));
        Mockito.when(resource.snapshotResource.getTask(subscription)).thenReturn(status);

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "");
        Assertions.assertEquals(3, snapshosts.size());
        checkAmiPart(snapshosts.get(1));

        // Finished task, unlisted AMI and not created AMI (user from AMI)
        Assertions.assertEquals("ligoj-admin", snapshosts.get(0).getAuthor().getId());
        Assertions.assertEquals("ami-00000004", snapshosts.get(0).getId());
        Assertions.assertFalse(snapshosts.get(0).isPending());
        Assertions.assertTrue(snapshosts.get(0).isAvailable());
        Assertions.assertEquals("available", snapshosts.get(0).getStatusText());
    }

    /**
     * Last snapshot task is locally not finished and AMI identifier is not yet known.
     */
    @Test
    public void findAllByNameOrIdTaskNoAmi() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all.xml");
        final VmSnapshotStatus status = new VmSnapshotStatus();
        status.setAuthor("ligoj-admin2");
        status.setLocked(subscriptionRepository.findOneExpected(subscription));
        Mockito.when(resource.snapshotResource.getTask(subscription)).thenReturn(status);

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "");
        Assertions.assertEquals(3, snapshosts.size());
        checkAmiPart(snapshosts.get(1));

        // Unfinished task, unlisted AMI and not created AMI
        Assertions.assertEquals("ligoj-admin2", snapshosts.get(0).getAuthor().getId());
        Assertions.assertNull(snapshosts.get(0).getId());
        Assertions.assertTrue(snapshosts.get(0).isPending());
        Assertions.assertFalse(snapshosts.get(0).isAvailable());
        Assertions.assertEquals("not-created", snapshosts.get(0).getStatusText());
    }

    /**
     * Last snapshot task is locally finished but the AMI is not yet listed.
     */
    @Test
    public void findAllByNameOrIdTaskFinishedNotListed() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all.xml");
        mockAws("Action=DescribeImages&Owner.1=self&ImageId.1=ami-00000004",
                "mock-server/aws/describe-images-00000004.xml");
        final VmSnapshotStatus status = new VmSnapshotStatus();
        status.setEnd(new Date());
        status.setAuthor("ligoj-admin2");
        status.setSnapshotInternalId("ami-00000004");
        status.setLocked(subscriptionRepository.findOneExpected(subscription));
        Mockito.when(resource.snapshotResource.getTask(subscription)).thenReturn(status);

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "");
        Assertions.assertEquals(3, snapshosts.size());
        checkAmiPart(snapshosts.get(1));

        // Unfinished task, unlisted AMI and not created AMI
        final Snapshot snapshot = snapshosts.get(0);
        Assertions.assertEquals("ligoj-admin2", snapshot.getAuthor().getId());
        Assertions.assertEquals("ami-00000004", snapshot.getId());
        Assertions.assertTrue(snapshot.isPending());
        Assertions.assertFalse(snapshot.isAvailable());
        Assertions.assertEquals("not-finished-remote", snapshot.getStatusText());
    }

    /**
     * Unfinished locally : still not finished
     */
    @Test
    public void completeStatusNoAmiId() throws IOException {
        mockAws("Action=DescribeImages&Owner.1=self&ImageId.1=ami-00000004",
                "mock-server/aws/describe-images-empty.xml");
        VmSnapshotStatus status = new VmSnapshotStatus();
        Assertions.assertFalse(status.isFinished());
        resource.completeStatus(status);
        Assertions.assertFalse(status.isFinished());
    }

    /**
     * Finished locally, lookup by id failed : finished remotely with failure
     */
    @Test
    public void completeStatusNoAmi() throws IOException {
        // Lookup by id failed
        mockAws("Action=DescribeImages&Owner.1=self&ImageId.1=ami-00000004",
                "mock-server/aws/describe-images-empty.xml");
        final VmSnapshotStatus status = new VmSnapshotStatus();
        status.setEnd(new Date());
        status.setAuthor("ligoj-admin2");
        status.setSnapshotInternalId("ami-00000004");
        status.setLocked(subscriptionRepository.findOneExpected(subscription));
        Mockito.when(resource.snapshotResource.getTask(subscription)).thenReturn(status);

        resource.completeStatus(status);

        Assertions.assertTrue(status.isFinished());
        Assertions.assertTrue(status.isFailed());
        Assertions.assertTrue(status.isFinishedRemote());
        Assertions.assertEquals("not-found", status.getStatusText());
    }

    /**
     * Finished locally, lookup by id succeed : not finished remotely
     */
    @Test
    public void completeStatusUnlisted() throws IOException {
        // Lookup by id succeed
        mockAws("Action=DescribeImages&Owner.1=self&ImageId.1=ami-00000004",
                "mock-server/aws/describe-images-00000004.xml");

        // Not yet listed
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all.xml");
        final VmSnapshotStatus status = new VmSnapshotStatus();
        status.setEnd(new Date());
        status.setAuthor("ligoj-admin2");
        status.setSnapshotInternalId("ami-00000004");
        status.setLocked(subscriptionRepository.findOneExpected(subscription));
        Mockito.when(resource.snapshotResource.getTask(subscription)).thenReturn(status);

        resource.completeStatus(status);

        Assertions.assertTrue(status.isFinished());
        Assertions.assertFalse(status.isFailed());
        Assertions.assertFalse(status.isFinishedRemote());
        Assertions.assertEquals("not-finished-remote", status.getStatusText());
    }

    /**
     * Finished locally, but not for a CREATE snapshot.
     */
    @Test
    void completeStatusNotCreate() {
        final VmSnapshotStatus status = new VmSnapshotStatus();
        status.setEnd(new Date());
        status.setAuthor("ligoj-admin2");
        status.setSnapshotInternalId("ami-00000004");
        status.setLocked(subscriptionRepository.findOneExpected(subscription));
        status.setOperation(SnapshotOperation.DELETE);
        Mockito.when(resource.snapshotResource.getTask(subscription)).thenReturn(status);

        // This would do nothing for this operation
        resource.completeStatus(status);

        // Status is not changed
        Assertions.assertFalse(status.isFinishedRemote());

    }

    /**
     * Finished locally, lookup by id succeed and listed : finished remotely
     */
    @Test
    public void completeStatus() throws IOException {
        // Lookup by id succeed
        mockAws("Action=DescribeImages&Owner.1=self&ImageId.1=ami-00000004",
                "mock-server/aws/describe-images-00000004.xml");

        // Not yet listed with correct tags
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all-with-00000004.xml");
        final VmSnapshotStatus status = new VmSnapshotStatus();
        status.setEnd(new Date());
        status.setAuthor("ligoj-admin2");
        status.setSnapshotInternalId("ami-00000004");
        status.setLocked(subscriptionRepository.findOneExpected(subscription));
        Mockito.when(resource.snapshotResource.getTask(subscription)).thenReturn(status);

        resource.completeStatus(status);

        Assertions.assertTrue(status.isFinished());
        Assertions.assertFalse(status.isFailed());
        Assertions.assertTrue(status.isFinishedRemote());
    }

    @Test
    public void findAllByNameOrIdInvalidContent() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all-invalid.html");
        Assertions.assertEquals("DescribeImages-failed",
                Assertions.assertThrows(BusinessException.class, () -> resource.findAllByNameOrId(subscription, ""))
                        .getMessage());
    }

    @Test
    public void findAllByNameOrIdFilterSnapShot() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all.xml");

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "snap-0a9e26b713aeec40e");
        Assertions.assertEquals(1, snapshosts.size());
        checkAmiPart(snapshosts.get(0));
    }

    @Test
    public void findAllByNameOrIdFilterId() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all.xml");

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "ami-00000001");
        Assertions.assertEquals(1, snapshosts.size());
        checkAmiPart(snapshosts.get(0));
    }

    @Test
    public void findAllByNameOrIdFilterName() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all.xml");

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "sample-ligo2");
        Assertions.assertEquals(1, snapshosts.size());
        checkAmiPart(snapshosts.get(0));
    }

    @Test
    public void findAllByNameOrIdInvalidDate() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all-invalid-date.xml");

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "n");
        Assertions.assertEquals(2, snapshosts.size());
        Assertions.assertEquals(0, checkAmiPart(snapshosts.get(1)).getDate().getTime());
    }

    @Test
    public void findAllByNameOrIdNotFoundAuthor() throws IOException {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all.xml");

        // Mock IAM
        resource.iamProvider = new IamProvider[] { Mockito.mock(IamProvider.class) };
        final IamConfiguration iamConfiguration = Mockito.mock(IamConfiguration.class);
        Mockito.doReturn(iamConfiguration).when(resource.iamProvider[0]).getConfiguration();
        final IUserRepository userRepository = Mockito.mock(IUserRepository.class);
        Mockito.doReturn(userRepository).when(iamConfiguration).getUserRepository();

        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "n");
        Assertions.assertEquals(2, snapshosts.size());
        Assertions.assertNull(checkAmiPart(snapshosts.get(0)).getAuthor().getCompany());
    }

    @Test
    public void findAllByNameOrIdInvalidVolume() throws Exception {
        mockAws("Action=DescribeImages&Owner.1=self&Filter.1.Name=tag:ligoj:subscription&Filter.1.Value="
                + subscription, "mock-server/aws/describe-images-all-invalid-volume.xml");

        // Only one volume snapshot match inside the AMI
        final List<Snapshot> snapshosts = resource.findAllByNameOrId(subscription, "snap-");
        Assertions.assertEquals(1, snapshosts.size());
        checkAmiPart(snapshosts.get(0));

        // Without filter, the other AMI matches, and does not contains any volume snapshot (invalid)
        final List<Snapshot> snapshostsNoFilter = resource.findAllByNameOrId(subscription, "");
        Assertions.assertEquals(2, snapshostsNoFilter.size());
        Assertions.assertEquals(0, snapshostsNoFilter.get(1).getVolumes().size());
    }

    @Test
    public void create() throws Exception {
        final VmSnapshotStatus status = mockStatus();
        mockAws("Action=CreateImage&NoReboot=false&InstanceId=i-12345678&Name=ligoj-snapshot/" + subscription + "/"
                + new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(status.getStart())
                + "&Description=Snapshot+created+from+Ligoj", "mock-server/aws/create-images.xml");
        mockAws("Action=CreateTags&ResourceId.1=ami-00000004&Tag.1.Key=ligoj:subscription&Tag.1.Value="
                + subscription + "&Tag.2.Key=ligoj:audit&Tag.2.Value=ligoj-admin",
                "mock-server/aws/create-tags.xml");

        // Main call
        resource.create(status);
        checkCreate(status);
    }

    @Test
    public void createNoReboot() throws Exception {
        final VmSnapshotStatus status = mockStatus();
        status.setStop(false);
        mockAws("Action=CreateImage&NoReboot=true&InstanceId=i-12345678&Name=ligoj-snapshot/" + subscription + "/"
                + new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(status.getStart())
                + "&Description=Snapshot+created+from+Ligoj", "mock-server/aws/create-images.xml");
        mockAws("Action=CreateTags&ResourceId.1=ami-00000004&Tag.1.Key=ligoj:subscription&Tag.1.Value="
                + subscription + "&Tag.2.Key=ligoj:audit&Tag.2.Value=ligoj-admin",
                "mock-server/aws/create-tags.xml");

        // Main call
        resource.create(status);
        checkCreate(status);
    }

    private void checkCreate(final VmSnapshotStatus status) {
        Assertions.assertTrue(status.isFinished());
        Assertions.assertFalse(status.isFailed());
        Assertions.assertEquals("checking-availability", status.getPhase());
        Assertions.assertEquals(2, status.getDone());
        Assertions.assertEquals(3, status.getWorkload());
        Assertions.assertEquals("ami-00000004", status.getSnapshotInternalId());
    }

    @Test
    public void createAmiFail() throws Exception {
        final VmSnapshotStatus status = mockStatus();

        // Main call
        resource.create(status);

        Assertions.assertTrue(status.isFinished());
        Assertions.assertTrue(status.isFailed());
        Assertions.assertTrue(status.isFinishedRemote());
        Assertions.assertEquals("creating-ami", status.getPhase());
        Assertions.assertEquals(VmAwsPluginResource.KEY + ":ami-create-failed", status.getStatusText());
        Assertions.assertEquals(0, status.getDone());
        Assertions.assertEquals(3, status.getWorkload());
        Assertions.assertNull(status.getSnapshotInternalId());
    }

    @Test
    public void deleteSearchingNotFound() throws SAXException, IOException, ParserConfigurationException {
        final VmSnapshotStatus status = mockDeleteStatus();

        // Main call
        resource.delete(status);

        Assertions.assertTrue(status.isFinished());
        Assertions.assertTrue(status.isFailed());
        Assertions.assertTrue(status.isFinishedRemote());
        Assertions.assertEquals("searching-ami", status.getPhase());
        Assertions.assertEquals(VmAwsPluginResource.KEY + ":ami-not-found", status.getStatusText());
        Assertions.assertEquals(0, status.getDone());
        Assertions.assertEquals(3, status.getWorkload());
        Assertions.assertEquals("ami-00000004", status.getSnapshotInternalId());
    }

    @Test
    public void deleteDeregisteringFailed() throws SAXException, IOException, ParserConfigurationException {
        final VmSnapshotStatus status = mockDeleteStatus();
        mockAws("Action=DescribeImages&Owner.1=self&ImageId.1=ami-00000004",
                "mock-server/aws/describe-images-00000004.xml");
        checkDeregisteringFail(status);
    }

    @Test
    public void deleteDeregisteringReturnFalse() throws SAXException, IOException, ParserConfigurationException {
        final VmSnapshotStatus status = mockDeleteStatus();
        mockAws("Action=DescribeImages&Owner.1=self&ImageId.1=ami-00000004",
                "mock-server/aws/describe-images-00000004.xml");
        mockAws("Action=DeregisterImage&ImageId=ami-00000004", "mock-server/aws/deregister-image-return-false.xml");
        checkDeregisteringFail(status);
    }

    private VmSnapshotStatus mockDeleteStatus() {
        final VmSnapshotStatus status = mockStatus();
        status.setOperation(SnapshotOperation.DELETE);
        status.setSnapshotInternalId("ami-00000004");
        return status;
    }

    private void checkDeregisteringFail(final VmSnapshotStatus status)
            throws SAXException, IOException, ParserConfigurationException {
        resource.delete(status);
        Assertions.assertTrue(status.isFinished());
        Assertions.assertTrue(status.isFailed());
        Assertions.assertTrue(status.isFinishedRemote());
        Assertions.assertEquals("deregistering-ami", status.getPhase());
        Assertions.assertEquals(VmAwsPluginResource.KEY + ":ami-unregistering-failed", status.getStatusText());
        Assertions.assertEquals(1, status.getDone());
        Assertions.assertEquals(3, status.getWorkload());
        Assertions.assertEquals("ami-00000004", status.getSnapshotInternalId());
    }

    @Test
    public void deleteSnapshotsFail() throws SAXException, IOException, ParserConfigurationException {
        final VmSnapshotStatus status = mockDeleteStatus();
        mockAws("Action=DescribeImages&Owner.1=self&ImageId.1=ami-00000004",
                "mock-server/aws/describe-images-00000004-multiple-volumes.xml");
        mockAws("Action=DeregisterImage&ImageId=ami-00000004", "mock-server/aws/deregister-image.xml");
        checkDeleteSnapshotsFail(status);
    }

    @Test
    public void deleteSnapshotsReturnFalse() throws SAXException, IOException, ParserConfigurationException {
        final VmSnapshotStatus status = mockDeleteStatus();
        mockAws("Action=DescribeImages&Owner.1=self&ImageId.1=ami-00000004",
                "mock-server/aws/describe-images-00000004-multiple-volumes.xml");
        mockAws("Action=DeregisterImage&ImageId=ami-00000004", "mock-server/aws/deregister-image.xml");
        mockAws("Action=DeleteSnapshot&SnapshotId.1=snap-0a9e26b713aeec40e&SnapshotId.2=snap-0a9e26b713aeec40f",
                "mock-server/aws/delete-snapshot-return-false.xml");
        checkDeleteSnapshotsFail(status);
    }

    private void checkDeleteSnapshotsFail(final VmSnapshotStatus status)
            throws SAXException, IOException, ParserConfigurationException {
        resource.delete(status);
        Assertions.assertTrue(status.isFinished());
        Assertions.assertTrue(status.isFailed());
        Assertions.assertTrue(status.isFinishedRemote());
        Assertions.assertEquals("deleting-snapshots", status.getPhase());
        Assertions.assertEquals(VmAwsPluginResource.KEY + ":ami-deleting-snapshots-failed", status.getStatusText());
        Assertions.assertEquals(2, status.getDone());
        Assertions.assertEquals(3, status.getWorkload());
        Assertions.assertEquals("ami-00000004", status.getSnapshotInternalId());
    }

    @Test
    public void delete() throws SAXException, IOException, ParserConfigurationException {
        final VmSnapshotStatus status = mockDeleteStatus();
        mockAws("Action=DescribeImages&Owner.1=self&ImageId.1=ami-00000004",
                "mock-server/aws/describe-images-00000004-multiple-volumes.xml");
        mockAws("Action=DeregisterImage&ImageId=ami-00000004", "mock-server/aws/deregister-image.xml");
        mockAws("Action=DeleteSnapshot&SnapshotId.1=snap-0a9e26b713aeec40e&SnapshotId.2=snap-0a9e26b713aeec40f",
                "mock-server/aws/delete-snapshot.xml");
        resource.delete(status);
        Assertions.assertTrue(status.isFinished());
        Assertions.assertFalse(status.isFailed());
        Assertions.assertTrue(status.isFinishedRemote());
        Assertions.assertEquals("deleting-snapshots", status.getPhase());
        Assertions.assertNull(status.getStatusText());
        Assertions.assertEquals(3, status.getDone());
        Assertions.assertEquals(3, status.getWorkload());
        Assertions.assertEquals("ami-00000004", status.getSnapshotInternalId());
    }

    @Test
    public void createTagsFail() throws SAXException, IOException, ParserConfigurationException {
        final VmSnapshotStatus status = mockStatus();
        mockAws("Action=CreateImage&NoReboot=false&InstanceId=i-12345678&Name=ligoj-snapshot/" + subscription + "/"
                + new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(status.getStart())
                + "&Description=Snapshot+created+from+Ligoj", "mock-server/aws/create-images.xml");
        checkCreateTagsFail(status);
    }

    @Test
    public void createTagsFailReturnFalse() throws Exception {
        final VmSnapshotStatus status = mockStatus();
        mockAws("Action=CreateImage&NoReboot=false&InstanceId=i-12345678&Name=ligoj-snapshot/" + subscription + "/"
                + new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(status.getStart())
                + "&Description=Snapshot+created+from+Ligoj", "mock-server/aws/create-images.xml");
        mockAws("Action=CreateTags&ResourceId.1=ami-00000004&Tag.1.Key=ligoj:subscription&Tag.1.Value="
                + subscription + "&Tag.2.Key=ligoj:audit&Tag.2.Value=ligoj-admin",
                "mock-server/aws/create-tags-return-false.xml");
        checkCreateTagsFail(status);
    }

    private void checkCreateTagsFail(final VmSnapshotStatus status)
            throws SAXException, IOException, ParserConfigurationException {
        // Main call
        resource.create(status);

        Assertions.assertTrue(status.isFinished());
        Assertions.assertTrue(status.isFailed());
        Assertions.assertTrue(status.isFinishedRemote());
        Assertions.assertEquals("tagging-ami", status.getPhase());
        Assertions.assertEquals(VmAwsPluginResource.KEY + ":ami-tag-failed", status.getStatusText());
        Assertions.assertEquals(1, status.getDone());
        Assertions.assertEquals(3, status.getWorkload());
        Assertions.assertEquals("ami-00000004", status.getSnapshotInternalId());
    }

    private VmSnapshotStatus mockStatus() {
        final VmSnapshotStatus status = new VmSnapshotStatus();
        status.setAuthor("ligoj-admin");
        status.setStop(true);
        status.setStart(DateUtils.newCalendar().getTime());
        status.setLocked(subscriptionRepository.findOneExpected(subscription));
        status.setOperation(SnapshotOperation.CREATE);

        Mockito.when(resource.snapshotResource.getTask(subscription)).thenReturn(status);
        Mockito.when(resource.snapshotResource.nextStep(ArgumentMatchers.eq(subscription),
                ArgumentMatchers.argThat(new ArgumentMatcher<Consumer<VmSnapshotStatus>>() {

                    @Override
                    public boolean matches(final Consumer<VmSnapshotStatus> f) {
                        f.accept(status);
                        return true;
                    }
                }))).thenReturn(null);
        Mockito.when(resource.snapshotResource.endTask(ArgumentMatchers.eq(subscription), ArgumentMatchers.eq(true),
                ArgumentMatchers.argThat(new ArgumentMatcher<Consumer<VmSnapshotStatus>>() {

                    @Override
                    public boolean matches(final Consumer<VmSnapshotStatus> f) {
                        f.accept(status);
                        status.setFailed(true);
                        status.setEnd(new Date());
                        return true;
                    }
                }))).thenReturn(null);
        Mockito.when(
                resource.snapshotResource.endTask(ArgumentMatchers.eq(subscription), ArgumentMatchers.eq(false),
                        ArgumentMatchers.argThat(new ArgumentMatcher<Consumer<VmSnapshotStatus>>() {

                            @Override
                            public boolean matches(final Consumer<VmSnapshotStatus> f) {
                                f.accept(status);
                                status.setEnd(new Date());
                                return true;
                            }
                        })))
                .thenReturn(null);
        return status;
    }

    private Snapshot checkAmiPartNoVolume(final Snapshot snapshot) {
        Assertions.assertEquals("Information", snapshot.getDescription());
        Assertions.assertTrue(snapshot.isAvailable());
        Assertions.assertFalse(snapshot.isPending());
        Assertions.assertEquals("ami-00000001", snapshot.getId());
        Assertions.assertEquals("sample-ligo2", snapshot.getName());
        Assertions.assertEquals("available", snapshot.getStatusText());
        Assertions.assertEquals("ligoj-admin", snapshot.getAuthor().getId());
        Assertions.assertNull(snapshot.getStopRequested());
        return snapshot;
    }

    private Snapshot checkAmiPart(final Snapshot snapshot) {
        Assertions.assertEquals(1, snapshot.getVolumes().size());
        Assertions.assertEquals(8, snapshot.getVolumes().get(0).getSize());
        Assertions.assertEquals("snap-0a9e26b713aeec40e", snapshot.getVolumes().get(0).getId());
        Assertions.assertEquals("/dev/sda1", snapshot.getVolumes().get(0).getName());
        return checkAmiPartNoVolume(snapshot);
    }

    private void mockAws(final String url, final String response) throws IOException {
        final Map<String, String> parameters = subscriptionResource.getParametersNoCheck(subscription);
        Mockito.when(resource.resource.processEC2(ArgumentMatchers.eq(subscription),
                ArgumentMatchers.argThat(new ArgumentMatcher<Function<Map<String, String>, String>>() {

                    @Override
                    public boolean matches(final Function<Map<String, String>, String> f) {
                        return f.apply(parameters).equals(url);
                    }
                }))).thenReturn(IOUtils.toString(new ClassPathResource(response).getInputStream(), "UTF-8"));
        Mockito.when(resource.resource.processEC2(ArgumentMatchers.eq(parameters), ArgumentMatchers.eq(url)))
                .thenReturn(IOUtils.toString(new ClassPathResource(response).getInputStream(), "UTF-8"));
    }

    /**
     * Return the subscription identifier of the given project. Assumes there is only one subscription for a service.
     */
    protected int getSubscription(final String project) {
        return getSubscription(project, VmAwsPluginResource.KEY);
    }

    @Override
    protected String getAuthenticationName() {
        return DEFAULT_USER;
    }
}