/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.channel.cc.commons.keymanagement;

import static java.nio.file.Files.createTempFile;
import static java.nio.file.Files.delete;
import static java.nio.file.Files.deleteIfExists;
import static java.nio.file.Files.exists;
import static java.nio.file.Files.write;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.security.KeyManagementException;
import java.security.KeyStore.PasswordProtection;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

/**
 * Tests of {@link NodeKeysActivatorImpl}.
 */
public class NodeKeysActivatorImplTest {
    private Path keyStoreFile;

    private Path passwordFile;

    private KeyManager manager;

    private NodeKeysActivatorImpl activator;

    @Before
    public void setUp() throws IOException {
        keyStoreFile = createTempFile("keys", ".p12");
        passwordFile = createTempFile("password", null);
        write(passwordFile, "password".getBytes(StandardCharsets.UTF_8));
        manager = mock(KeyManager.class);
        activator = new NodeKeysActivatorImpl(manager);
    }

    @After
    public void tearDown() throws IOException {
        deleteIfExists(keyStoreFile);
        deleteIfExists(passwordFile);
    }

    @Test
    public void testActivateNodeKeysKeyManagerPathStringPath()
            throws IOException, KeyNotFoundException,
            InvalidPasswordException, KeyManagementException {
        try {
            activator.activateNodeKeys(keyStoreFile, "alias",
                passwordFile);
        } finally {
            assertFalse(exists(passwordFile));
        }
        verify(manager, never()).createAndActivateNodeKeys(any(Path.class),
            anyString(), any());
    }

    @Test
    public void testActivateNodeKeysKeyManagerPathStringPathNoDatabase()
            throws IOException, KeyNotFoundException,
            InvalidPasswordException, KeyManagementException {
        doThrow(new KeyNotFoundException("test")).when(manager)
            .activateNodeKeys(any());
        doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocation)
                    throws Throwable {
                PasswordProtection protection =
                    invocation.getArgumentAt(2, PasswordProtection.class);
                assertEquals("password",
                    new String(protection.getPassword()));
                return null;
            }
        }).when(manager).createAndActivateNodeKeys(any(Path.class),
            eq("alias"), any());
        try {
            activator.activateNodeKeys(keyStoreFile, "alias",
                passwordFile);
        } finally {
            assertFalse(exists(passwordFile));
        }
        verify(manager).createAndActivateNodeKeys(eq(keyStoreFile),
            eq("alias"),
            argThat(new ArgumentMatcher<PasswordProtection>() {
                @Override
                public boolean matches(Object argument) {
                    return ((PasswordProtection) argument).isDestroyed();
                }
            }));
    }

    @Test(expected = NoSuchFileException.class)
    public void testActivateNodeKeysKeyManagerPathStringPathNoDatabaseFiles()
            throws IOException, KeyNotFoundException,
            InvalidPasswordException, KeyManagementException {
        delete(keyStoreFile);
        delete(passwordFile);
        doThrow(new KeyNotFoundException("test")).when(manager)
            .activateNodeKeys(any());
        try {
            activator.activateNodeKeys(keyStoreFile, "alias",
                passwordFile);
        } finally {
            assertFalse(exists(passwordFile));
        }
    }

    @Test(expected = InvalidPasswordException.class)
    public void testActivateNodeKeysKeyManagerPathStringPathNoDatabaseInvalidPassword()
            throws IOException, KeyNotFoundException,
            InvalidPasswordException, KeyManagementException {
        doThrow(new KeyNotFoundException("test")).when(manager)
            .activateNodeKeys(any());
        doThrow(new InvalidPasswordException("test")).when(manager)
            .createAndActivateNodeKeys(any(Path.class), anyString(),
                any());
        try {
            activator.activateNodeKeys(keyStoreFile, "alias",
                passwordFile);
        } finally {
            assertFalse(exists(passwordFile));
        }
    }

    @Test(expected = NoSuchFileException.class)
    public void testActivateNodeKeysKeyManagerPathStringPathNoDatabaseKeyStore()
            throws IOException, KeyNotFoundException,
            InvalidPasswordException, KeyManagementException {
        delete(keyStoreFile);
        doThrow(new KeyNotFoundException("test")).when(manager)
            .activateNodeKeys(any());
        doThrow(new NoSuchFileException("test")).when(manager)
            .createAndActivateNodeKeys(any(Path.class), anyString(),
                any());
        try {
            activator.activateNodeKeys(keyStoreFile, "alias",
                passwordFile);
        } finally {
            assertFalse(exists(passwordFile));
        }
    }

    @Test(expected = NoSuchFileException.class)
    public void testActivateNodeKeysKeyManagerPathStringPathNoDatabasePassword()
            throws IOException, KeyNotFoundException,
            InvalidPasswordException, KeyManagementException {
        delete(passwordFile);
        doThrow(new KeyNotFoundException("test")).when(manager)
            .activateNodeKeys(any());
        try {
            activator.activateNodeKeys(keyStoreFile, "alias",
                passwordFile);
        } finally {
            assertFalse(exists(passwordFile));
        }
    }

    @Test(expected = NoSuchFileException.class)
    public void testActivateNodeKeysKeyManagerPathStringPathNoFiles()
            throws IOException, KeyNotFoundException,
            InvalidPasswordException, KeyManagementException {
        delete(keyStoreFile);
        delete(passwordFile);
        try {
            activator.activateNodeKeys(keyStoreFile, "alias",
                passwordFile);
        } finally {
            assertFalse(exists(passwordFile));
        }
    }

    @Test
    public void testActivateNodeKeysKeyManagerPathStringPathNoKeyStore()
            throws IOException, KeyNotFoundException,
            InvalidPasswordException, KeyManagementException {
        delete(keyStoreFile);
        try {
            activator.activateNodeKeys(keyStoreFile, "alias",
                passwordFile);
        } finally {
            assertFalse(exists(passwordFile));
        }
        verify(manager, never()).createAndActivateNodeKeys(any(Path.class),
            anyString(), any());
    }

    @Test(expected = InvalidPasswordException.class)
    public void testActivateNodeKeysKeyManagerPathStringPathNoKeyStoreInvalidPassword()
            throws IOException, KeyNotFoundException,
            InvalidPasswordException, KeyManagementException {
        delete(keyStoreFile);
        doThrow(new InvalidPasswordException("test")).when(manager)
            .activateNodeKeys(any());
        try {
            activator.activateNodeKeys(keyStoreFile, "alias",
                passwordFile);
        } finally {
            assertFalse(exists(passwordFile));
        }
    }
}
