/*
 * 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.slogger.event;

import static java.util.Collections.singletonMap;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.apache.log4j.NDC;
import org.apache.log4j.spi.LoggingEvent;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * Tests of {@link SecureLoggingEvent}.
 * 
 * @author aakimov
 */
public class SecureLoggingEventTest {

  @AfterClass
  public static void afterClass() {
    NDC.pop();
    MDC.remove("foo");;
  }

  @BeforeClass
  public static void beforeClass() {
    NDC.push("cool");
    MDC.put("foo", "bar");
  }

  @Test
  public void testAddProperties() {
    SecureLoggingEvent original = SecureLoggingEvent.newHMacInstance().addProperty("key1", "value1")
        .addProperty("key2", "value1");

    SecureLoggingEvent modified = original.addProperties(singletonMap("key2", "value2"));
    assertSameLoggingEvents(original, modified);
    Map<String, String> properties = modified.getSecureMessage().properties();
    assertEquals(2, properties.size());
    assertEquals("value1", properties.get("key1"));
    assertEquals("value2", properties.get("key2"));
    assertNull(modified.getNDC());
    assertNull(modified.getMDC("foo"));
  }

  @Test
  public void testAddProperty() {
    SecureLoggingEvent original = SecureLoggingEvent.newHMacInstance();
    original = original.addProperty("key1", "value1").addProperty("key2", "value1");

    SecureLoggingEvent modified = original.addProperty("key2", "value2");
    assertSameLoggingEvents(original, modified);
    Map<String, String> properties = modified.getSecureMessage().properties();
    assertEquals(2, properties.size());
    assertEquals("value1", properties.get("key1"));
    assertEquals("value2", properties.get("key2"));
    assertNull(modified.getNDC());
    assertNull(modified.getMDC("foo"));
  }

  @Test
  public void testGetRenderedMessage() {
    SecureLoggingEvent event = SecureLoggingEvent.newHMacInstance().addProperty("key1", "value1")
        .addProperty("key2", "value2");
    SecureMessage message = SecureMessage.newRenderedInstance(event.getRenderedMessage());
    assertEquals(message.text(), event.getSecureMessage().text());
    assertEquals(message.properties(), event.getSecureMessage().properties());
  }

  @Test
  public void testGetThrowableEventRep() {
    LoggingEvent original = new LoggingEvent(getClass().getName(), Logger.getRootLogger(),
        Level.WARN, "message", new IOException("test"));

    List<SecureLoggingEvent> events =
        SecureLoggingEvent.newClientInstance(original).getThrowableEventRep();
    String[] strings = original.getThrowableStrRep();
    assertEquals(strings.length, events.size());
    for (int i = 0; i < strings.length; i++) {
      assertEquals(strings[i], events.get(i).getRenderedMessage());
    }
  }

  @Test
  public void testNewCheckpointInstance() {
    long before = System.currentTimeMillis();
    SecureLoggingEvent event = SecureLoggingEvent.newCheckpointInstance();
    long after = System.currentTimeMillis();
    assertEquals(SecureLoggingEvent.class.getName(), event.getFQNOfLoggerClass());
    assertEquals("root", event.getLogger().getName());
    assertTrue(before <= event.getTimeStamp() && event.getTimeStamp() <= after);
    assertEquals(Level.DEBUG, event.getLevel());
    assertEquals("Checkpoint Entry", event.getSecureMessage().text());
    assertTrue(event.isCheckpoint());
    assertEquals(Thread.currentThread().getName(), event.getThreadName());
    assertNull(event.getThrowableInformation());
    assertNull(event.getNDC());
    assertNull(event.getMDC("foo"));
  }

  @Test
  public void testNewClientInstance() {
    LoggingEvent expected = new LoggingEvent(getClass().getName(), Logger.getRootLogger(),
        Level.WARN, "message", new IOException("test"));
    SecureLoggingEvent actual = SecureLoggingEvent.newClientInstance(expected);
    assertSameLoggingEvents(expected, actual);
    assertFalse(actual.isCheckpoint());
    assertEquals("cool", actual.getNDC());
    assertEquals("bar", actual.getMDC("foo"));
  }

  @Test
  public void testNewFileInstance() {
    long before = System.currentTimeMillis();
    File file = new File("test.log");
    SecureLoggingEvent event = SecureLoggingEvent.newFileInstance(file);
    long after = System.currentTimeMillis();
    assertEquals(SecureLoggingEvent.class.getName(), event.getFQNOfLoggerClass());
    assertEquals("root", event.getLogger().getName());
    assertTrue(before <= event.getTimeStamp() && event.getTimeStamp() <= after);
    assertEquals(Level.DEBUG, event.getLevel());
    assertEquals("Log File Name: " + file.getAbsolutePath(), event.getSecureMessage().text());
    assertFalse(event.isCheckpoint());
    assertEquals(Thread.currentThread().getName(), event.getThreadName());
    assertNull(event.getThrowableInformation());
    assertNull(event.getNDC());
    assertNull(event.getMDC("foo"));
  }

  @Test
  public void testNewHMacInstance() {
    long before = System.currentTimeMillis();
    SecureLoggingEvent event = SecureLoggingEvent.newHMacInstance();
    long after = System.currentTimeMillis();
    assertEquals(SecureLoggingEvent.class.getName(), event.getFQNOfLoggerClass());
    assertEquals("root", event.getLogger().getName());
    assertTrue(before <= event.getTimeStamp() && event.getTimeStamp() <= after);
    assertEquals(Level.DEBUG, event.getLevel());
    assertEquals("New Secret Key generated.", event.getSecureMessage().text());
    assertFalse(event.isCheckpoint());
    assertEquals(Thread.currentThread().getName(), event.getThreadName());
    assertNull(event.getThrowableInformation());
    assertNull(event.getNDC());
    assertNull(event.getMDC("foo"));
  }

  @Test
  public void testRemoveProperties() {
    SecureLoggingEvent original = SecureLoggingEvent.newHMacInstance().addProperty("key1", "value1")
        .addProperty("key2", "value2");

    SecureLoggingEvent modified = original.removeProperties();
    assertSameLoggingEvents(original, modified);
    assertTrue(modified.getSecureMessage().properties().isEmpty());
    assertNull(modified.getNDC());
    assertNull(modified.getMDC("foo"));
  }

  @Test
  public void testRemovePropertyString() {
    SecureLoggingEvent original = SecureLoggingEvent.newHMacInstance().addProperty("key1", "value1")
        .addProperty("key2", "value2");

    SecureLoggingEvent modified = original.removeProperty("key2");
    assertSameLoggingEvents(original, modified);
    Map<String, String> properties = modified.getSecureMessage().properties();
    assertEquals(1, properties.size());
    assertEquals("value1", properties.get("key1"));
    assertNull(modified.getNDC());
    assertNull(modified.getMDC("foo"));
  }

  private void assertSameLoggingEvents(LoggingEvent expected, LoggingEvent actual) {
    assertEquals(expected.getFQNOfLoggerClass(), actual.getFQNOfLoggerClass());
    assertEquals(expected.getLogger(), actual.getLogger());
    assertEquals(expected.getLevel(), actual.getLevel());
    assertEquals(expected.getThreadName(), actual.getThreadName());
    assertEquals(expected.getTimeStamp(), actual.getTimeStamp());
    assertEquals(expected.getThrowableInformation(), actual.getThrowableInformation());
    assertEquals(expected.getLocationInformation(), actual.getLocationInformation());
    assertEquals(expected.getNDC(), actual.getNDC());
    assertEquals(expected.getMDC("foo"), actual.getMDC("foo"));
  }
}
