/*
 * 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.cryptolib.asymmetric.keypair.openssl;

import static java.util.Arrays.asList;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.Before;
import org.junit.Test;

import mockit.Expectations;
import mockit.Injectable;

/**
 * Tests of {@link OpenSSLImpl}.
 */
public class OpenSSLImplTest {
    private static byte[] OUTPUT = {1, 2, 3 };

    @Injectable
    private Process process;

    @Injectable
    private ProcessFactory factory;

    private OpenSSLImpl openSSL;

    @Before
    public void setUp() {
        openSSL = new OpenSSLImpl(factory);
    }

    @Test
    public void testGenpkey()
            throws OpenSSLException, IOException, InterruptedException {
        new Expectations() {
            {
                List<String> command =
                    asList("openssl", "genpkey", "-outform", "PEM",
                        "-algorithm", "RSA", "-pkeyopt", "key:value");
                factory.createProcess(withEqual(command));
                result = process;
                process.getInputStream();
                result = new ByteArrayInputStream(OUTPUT);
                process.waitFor();
                result = 0;
            }
        };
        Map<String, String> options = new HashMap<>();
        options.put("key", "value");
        assertArrayEquals(OUTPUT, openSSL.genpkey("RSA", options));
    }

    @Test
    public void testVersion()
            throws OpenSSLException, IOException, InterruptedException {
        new Expectations() {
            {
                List<String> command = asList("openssl", "version");
                factory.createProcess(withEqual(command));
                result = process;
                process.getInputStream();
                result = new ByteArrayInputStream(OUTPUT);
                process.waitFor();
                result = 0;
            }
        };
        assertEquals(new String(OUTPUT, StandardCharsets.UTF_8),
            openSSL.version());
    }

    @Test(expected = OpenSSLException.class)
    public void testVersionErrorExitCode()
            throws OpenSSLException, IOException, InterruptedException {
        new Expectations() {
            {
                List<String> command = asList("openssl", "version");
                factory.createProcess(withEqual(command));
                result = process;
                process.getInputStream();
                result = new ByteArrayInputStream(OUTPUT);
                process.waitFor();
                result = 1;
            }
        };
        openSSL.version();
    }

    @Test(expected = OpenSSLException.class)
    public void testVersionInterrupted()
            throws OpenSSLException, IOException, InterruptedException {
        new Expectations() {
            {
                List<String> command = asList("openssl", "version");
                factory.createProcess(withEqual(command));
                result = process;
                process.getInputStream();
                result = new ByteArrayInputStream(OUTPUT);
                process.waitFor();
                result = new InterruptedException("test");
            }
        };
        try {
            openSSL.version();
        } finally {
            assertTrue(Thread.interrupted());
        }
    }

    @Test(expected = OpenSSLException.class)
    public void testVersionIOExceptionDuringLaunch()
            throws OpenSSLException, IOException, InterruptedException {
        new Expectations() {
            {
                List<String> command = asList("openssl", "version");
                factory.createProcess(withEqual(command));
                result = new IOException("test");
            }
        };
        openSSL.version();
    }

    @Test(expected = OpenSSLException.class)
    public void testVersionIOExceptionDuringRead()
            throws OpenSSLException, IOException, InterruptedException {
        new Expectations() {
            {
                List<String> command = asList("openssl", "version");
                factory.createProcess(withEqual(command));
                result = process;
                process.getInputStream();
                result = new InputStream() {
                    @Override
                    public int read() throws IOException {
                        throw new IOException("test");
                    }
                };
            }
        };
        openSSL.version();
    }
}
