content: /*
   * Copyright 1999-2018 Alibaba Group Holding Ltd.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.alibaba.nacos.common.tls;
  
  import org.junit.jupiter.api.AfterEach;
  import org.junit.jupiter.api.BeforeAll;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  import org.junit.jupiter.api.extension.ExtendWith;
  import org.mockito.Mock;
  import org.mockito.junit.jupiter.MockitoExtension;
  import org.mockito.junit.jupiter.MockitoSettings;
  import org.mockito.quality.Strictness;
  import org.mockito.stubbing.Answer;
  
  import java.io.File;
  import java.io.IOException;
  import java.lang.reflect.Field;
  import java.lang.reflect.InvocationTargetException;
  import java.util.Map;
  import java.util.concurrent.ScheduledExecutorService;
  import java.util.concurrent.atomic.AtomicBoolean;
  import java.util.concurrent.atomic.AtomicReference;
  
  import static org.junit.jupiter.api.Assertions.assertEquals;
  import static org.junit.jupiter.api.Assertions.assertFalse;
  import static org.junit.jupiter.api.Assertions.assertTrue;
  import static org.mockito.ArgumentMatchers.any;
  import static org.mockito.ArgumentMatchers.anyLong;
  import static org.mockito.Mockito.doAnswer;
  import static org.mockito.Mockito.times;
  import static org.mockito.Mockito.verify;
  
  @ExtendWith(MockitoExtension.class)
  // todo remove this
  @MockitoSettings(strictness = Strictness.LENIENT)
  class TlsFileWatcherTest {
      
      static Field watchFilesMapField;
      
      static Field fileMd5MapField;
      
      static Field serviceField;
      
      static Field startedField;
      
      File tempFile;
      
      @Mock
      ScheduledExecutorService executorService;
      
      @BeforeAll
      static void setUpBeforeClass()
              throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
          watchFilesMapField = TlsFileWatcher.getInstance().getClass().getDeclaredField("watchFilesMap");
          watchFilesMapField.setAccessible(true);
          
          fileMd5MapField = TlsFileWatcher.getInstance().getClass().getDeclaredField("fileMd5Map");
          fileMd5MapField.setAccessible(true);
          
          serviceField = TlsFileWatcher.getInstance().getClass().getDeclaredField("service");
          serviceField.setAccessible(true);
          
          startedField = TlsFileWatcher.getInstance().getClass().getDeclaredField("started");
          startedField.setAccessible(true);
      }
      
      @BeforeEach
      void setUp() throws IOException, IllegalAccessException {
          tempFile = new File("test.txt");
          tempFile.createNewFile();
          serviceField.set(TlsFileWatcher.getInstance(), executorService);
          startedField.set(TlsFileWatcher.getInstance(), new AtomicBoolean(false));
          Answer<?> answer = invocationOnMock -> {
              Runnable runnable = (Runnable) invocationOnMock.getArguments()[0];
              runnable.run();
              return null;
          };
          doAnswer(answer).when(executorService).scheduleAtFixedRate(any(), anyLong(), anyLong(), any());
      }
      
      @AfterEach
      void tearDown() throws IllegalAccessException {
          ((Map<?, ?>) watchFilesMapField.get(TlsFileWatcher.getInstance())).clear();
          ((Map<?, ?>) fileMd5MapField.get(TlsFileWatcher.getInstance())).clear();
          tempFile.deleteOnExit();
      }
      
      @Test
      void testAddFileChangeListener1() throws IOException, IllegalAccessException {
          TlsFileWatcher.getInstance().addFileChangeListener(filePath -> {
          }, "not/exist/path");
          
          assertTrue(((Map<?, ?>) watchFilesMapField.get(TlsFileWatcher.getInstance())).isEmpty());
          assertTrue(((Map<?, ?>) fileMd5MapField.get(TlsFileWatcher.getInstance())).isEmpty());
      }
      
      @Test
      void testAddFileChangeListener2() throws IOException, IllegalAccessException {
          TlsFileWatcher.getInstance().addFileChangeListener(filePath -> {
          }, (String) null);
          
          assertTrue(((Map<?, ?>) watchFilesMapField.get(TlsFileWatcher.getInstance())).isEmpty());
          assertTrue(((Map<?, ?>) fileMd5MapField.get(TlsFileWatcher.getInstance())).isEmpty());
      }
      
      @Test
      void testAddFileChangeListener3() throws IOException, IllegalAccessException {
          TlsFileWatcher.getInstance().addFileChangeListener(filePath -> {
          }, tempFile.getPath());
          
          assertEquals(1, ((Map<?, ?>) watchFilesMapField.get(TlsFileWatcher.getInstance())).size());
          assertEquals(1, ((Map<?, ?>) fileMd5MapField.get(TlsFileWatcher.getInstance())).size());
      }
      
      @Test
      void testStartGivenTlsFileNotChangeThenNoNotify() throws IllegalAccessException, InterruptedException, IOException {
          // given
          AtomicBoolean notified = new AtomicBoolean(false);
          TlsFileWatcher.getInstance().addFileChangeListener(filePath -> notified.set(true), tempFile.getPath());
          
          // when
          TlsFileWatcher.getInstance().start();
          
          // then
          assertFalse(notified.get());
      }
      
      @Test
      void testStartGivenTlsFileChangeThenNotifyTheChangeFilePath() throws IllegalAccessException, IOException {
          // given
          AtomicBoolean notified = new AtomicBoolean(false);
          AtomicReference<String> changedFilePath = new AtomicReference<>();
          TlsFileWatcher.getInstance().addFileChangeListener(filePath -> {
              notified.set(true);
              changedFilePath.set(filePath);
          }, tempFile.getPath());
          ((Map<String, String>) fileMd5MapField.get(TlsFileWatcher.getInstance())).put("test.txt", "");
          
          // when
          TlsFileWatcher.getInstance().start();
          
          // then
          assertTrue(notified.get());
          assertEquals("test.txt", changedFilePath.get());
      }
      
      @Test
      void testStartGivenTaskIsAlreadyRunThenNotRunAgain() {
          TlsFileWatcher.getInstance().start();
          TlsFileWatcher.getInstance().start();
          
          verify(executorService, times(1)).scheduleAtFixedRate(any(), anyLong(), anyLong(), any());
      }
  }