github.com/imyousuf/webhook-broker@v0.1.2/config/cliconfig_test.go (about)

     1  package config
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io/ioutil"
     7  	"math/rand"
     8  	"os"
     9  	"strconv"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/fsnotify/fsnotify"
    15  	"github.com/rs/zerolog/log"
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  func randomString() string {
    20  	return strconv.Itoa(randomizer.Int())
    21  }
    22  
    23  var (
    24  	wdTestPath                 = "../testdatadir/"
    25  	randomizer                 = rand.New(rand.NewSource(time.Now().UnixNano()))
    26  	notificationFilePath       = wdTestPath + "webhook-broker.change-test_" + randomString() + ".cfg"
    27  	noChangeFilePath           = wdTestPath + "webhook-broker.no-notify_" + randomString() + ".cfg"
    28  	removeFilePath             = wdTestPath + "webhook-broker.remove_" + randomString() + ".cfg"
    29  	hashTestPath               = wdTestPath + "webhook-broker.hash-err_" + randomString() + ".cfg"
    30  	truncateTestPath           = wdTestPath + "webhook-broker.truncate_" + randomString() + ".cfg"
    31  	notificationInitialContent = `[broker]
    32  	max-message-queue-size=10000000
    33  	`
    34  	notificationDifferentContent = `[broker]
    35  	max-message-queue-size=1
    36  	`
    37  )
    38  
    39  func writeToFile(filePath, content string) (err error) {
    40  	err = ioutil.WriteFile(filePath, []byte(content), 0644)
    41  	return err
    42  }
    43  
    44  func TestCLIConfigPathChangeNotification(t *testing.T) {
    45  	t.Run("NotifiedOnFileChange", func(t *testing.T) {
    46  		err := writeToFile(notificationFilePath, notificationInitialContent)
    47  		if err != nil {
    48  			log.Fatal().Err(err).Msg("could not write to file")
    49  		}
    50  		cliConfig := &CLIConfig{ConfigPath: notificationFilePath}
    51  		var wg sync.WaitGroup
    52  		wg.Add(1)
    53  		cliConfig.NotifyOnConfigFileChange(func() {
    54  			wg.Done()
    55  		})
    56  		defer cliConfig.StopWatcher()
    57  		assert.True(t, cliConfig.watcherStarted)
    58  		time.Sleep(5 * time.Millisecond)
    59  		err = writeToFile(notificationFilePath, notificationDifferentContent)
    60  		if err != nil {
    61  			log.Fatal().Err(err).Msg("could not write to file")
    62  		}
    63  		wg.Wait()
    64  	})
    65  	t.Run("NoNotifyOnFileContentUnchanged", func(t *testing.T) {
    66  		err := writeToFile(noChangeFilePath, notificationInitialContent)
    67  		if err != nil {
    68  			log.Fatal().Err(err).Msg("could not write to file")
    69  		}
    70  		cliConfig := &CLIConfig{ConfigPath: noChangeFilePath}
    71  		var wg sync.WaitGroup
    72  		cliConfig.NotifyOnConfigFileChange(func() {
    73  			wg.Done()
    74  		})
    75  		defer cliConfig.StopWatcher()
    76  		assert.True(t, cliConfig.watcherStarted)
    77  		time.Sleep(1 * time.Millisecond)
    78  		err = writeToFile(noChangeFilePath, notificationInitialContent)
    79  		if err != nil {
    80  			log.Fatal().Err(err).Msg("could not write to file")
    81  		}
    82  		time.Sleep(3 * time.Millisecond)
    83  		wg.Wait()
    84  	})
    85  	t.Run("NoNotifyOnFileTruncation", func(t *testing.T) {
    86  		var buf bytes.Buffer
    87  		oldLogger := log.Logger
    88  		log.Logger = log.Output(&buf)
    89  		defer func() {
    90  			log.Logger = oldLogger
    91  		}()
    92  		err := writeToFile(noChangeFilePath, notificationInitialContent)
    93  		if err != nil {
    94  			log.Fatal().Err(err).Msg("could not write to file")
    95  		}
    96  		cliConfig := &CLIConfig{ConfigPath: noChangeFilePath}
    97  		var wg sync.WaitGroup
    98  		cliConfig.NotifyOnConfigFileChange(func() {
    99  			wg.Done()
   100  		})
   101  		defer cliConfig.StopWatcher()
   102  		assert.True(t, cliConfig.watcherStarted)
   103  		time.Sleep(1 * time.Millisecond)
   104  		err = writeToFile(noChangeFilePath, "")
   105  		if err != nil {
   106  			log.Fatal().Err(err).Msg("could not write to file")
   107  		}
   108  		time.Sleep(3 * time.Millisecond)
   109  		wg.Wait()
   110  		assert.Contains(t, buf.String(), "truncation of config file not expected")
   111  		assert.Contains(t, buf.String(), errTruncatedConfigFile.Error())
   112  	})
   113  	t.Run("NothingHappensOnFileRemoval", func(t *testing.T) {
   114  		/*
   115  			Nothing will happen when the file is removed and the worker will stop,
   116  			this is primarily for code coverage nothing meaningful to assert other
   117  			than the fact that if the file is created again subsequently then no
   118  			change will be captured henceforth
   119  		*/
   120  		err := writeToFile(removeFilePath, notificationInitialContent)
   121  		if err != nil {
   122  			log.Fatal().Err(err).Msg("could not write to file")
   123  		}
   124  		cliConfig := &CLIConfig{ConfigPath: removeFilePath}
   125  		var wg sync.WaitGroup
   126  		cliConfig.NotifyOnConfigFileChange(func() {
   127  			wg.Done()
   128  		})
   129  		defer cliConfig.StopWatcher()
   130  		assert.True(t, cliConfig.watcherStarted)
   131  		time.Sleep(1 * time.Millisecond)
   132  		err = os.Remove(removeFilePath)
   133  		if err != nil {
   134  			log.Fatal().Err(err).Msg("could not write to file")
   135  		}
   136  		time.Sleep(3 * time.Millisecond)
   137  		wg.Wait()
   138  	})
   139  	t.Run("NoFilePathTest", func(t *testing.T) {
   140  		var buf bytes.Buffer
   141  		oldLogger := log.Logger
   142  		log.Logger = log.Output(&buf)
   143  		oldDir, _ := os.Getwd()
   144  		os.Chdir(wdTestPath)
   145  		defer func() {
   146  			os.Chdir(oldDir)
   147  			log.Logger = oldLogger
   148  		}()
   149  		cliConfig := &CLIConfig{}
   150  		var wg sync.WaitGroup
   151  		cliConfig.NotifyOnConfigFileChange(func() {
   152  			wg.Done()
   153  		})
   154  		defer cliConfig.StopWatcher()
   155  		time.Sleep(1 * time.Millisecond)
   156  		assert.Contains(t, buf.String(), errNoFileToWatch.Error())
   157  		assert.Contains(t, buf.String(), "could not find any file to watch")
   158  	})
   159  	t.Run("HashErrors", func(t *testing.T) {
   160  		var buf bytes.Buffer
   161  		oldLogger := log.Logger
   162  		log.Logger = log.Output(&buf)
   163  		defer func() {
   164  			log.Logger = oldLogger
   165  		}()
   166  		expectedErr := errors.New("hash error from test")
   167  		// Initial hash error
   168  		oldGetHash := getFileHash
   169  		errHashFn := func(filePath string) (string, error) {
   170  			return "", expectedErr
   171  		}
   172  		defer func() { getFileHash = oldGetHash }()
   173  		getFileHash = errHashFn
   174  		err := writeToFile(hashTestPath, notificationInitialContent)
   175  		if err != nil {
   176  			log.Fatal().Err(err).Msg("could not write to file")
   177  		}
   178  		cliConfig := &CLIConfig{ConfigPath: hashTestPath}
   179  		var wg sync.WaitGroup
   180  		cliConfig.NotifyOnConfigFileChange(func() {
   181  			wg.Done()
   182  		})
   183  		defer cliConfig.StopWatcher()
   184  		assert.True(t, cliConfig.watcherStarted)
   185  		time.Sleep(1 * time.Millisecond)
   186  		assert.Contains(t, buf.String(), expectedErr.Error())
   187  		assert.Contains(t, buf.String(), "could not generate original config file hash")
   188  		getFileHash = oldGetHash
   189  		cliConfig.watcherStarted = false
   190  		cliConfig.NotifyOnConfigFileChange(func() {
   191  			wg.Done()
   192  		})
   193  		assert.True(t, cliConfig.watcherStarted)
   194  		time.Sleep(1 * time.Millisecond)
   195  		getFileHash = errHashFn
   196  		err = writeToFile(hashTestPath, notificationInitialContent)
   197  		time.Sleep(1 * time.Millisecond)
   198  		assert.Contains(t, buf.String(), expectedErr.Error())
   199  		assert.Contains(t, buf.String(), "could not generate file hash on change")
   200  	})
   201  	t.Run("OpenFileErrorInGetHash", func(t *testing.T) {
   202  		_, err := getFileHash(wdTestPath + ConfigFilename)
   203  		assert.NotNil(t, err)
   204  	})
   205  	t.Run("OpenFileErrorInGetHashInline", func(t *testing.T) {
   206  		oldCreateWatcher := createNewWatcher
   207  		var buf bytes.Buffer
   208  		oldLogger := log.Logger
   209  		log.Logger = log.Output(&buf)
   210  		defer func() {
   211  			createNewWatcher = oldCreateWatcher
   212  			log.Logger = oldLogger
   213  		}()
   214  		expectedErr := errors.New("create watcher error from test")
   215  		createNewWatcher = func() (*fsnotify.Watcher, error) {
   216  			return nil, expectedErr
   217  		}
   218  		// Initial hash error
   219  		err := writeToFile(hashTestPath, notificationInitialContent)
   220  		if err != nil {
   221  			log.Fatal().Err(err).Msg("could not write to file")
   222  		}
   223  		cliConfig := &CLIConfig{ConfigPath: hashTestPath}
   224  		var wg sync.WaitGroup
   225  		cliConfig.NotifyOnConfigFileChange(func() {
   226  			wg.Done()
   227  		})
   228  		assert.True(t, cliConfig.watcherStarted)
   229  		time.Sleep(1 * time.Millisecond)
   230  		assert.Contains(t, buf.String(), expectedErr.Error())
   231  		assert.Contains(t, buf.String(), "could not setup watcher")
   232  	})
   233  	t.Run("PassErrorToWatcher", func(t *testing.T) {
   234  		oldCreateWatcher := createNewWatcher
   235  		var buf bytes.Buffer
   236  		oldLogger := log.Logger
   237  		log.Logger = log.Output(&buf)
   238  		defer func() {
   239  			createNewWatcher = oldCreateWatcher
   240  			log.Logger = oldLogger
   241  		}()
   242  		expectedErr := errors.New("manual watch error from test")
   243  		watcher, _ := fsnotify.NewWatcher()
   244  		createNewWatcher = func() (*fsnotify.Watcher, error) {
   245  			return watcher, nil
   246  		}
   247  		// Initial hash error
   248  		err := writeToFile(hashTestPath, notificationInitialContent)
   249  		if err != nil {
   250  			log.Fatal().Err(err).Msg("could not write to file")
   251  		}
   252  		cliConfig := &CLIConfig{ConfigPath: hashTestPath}
   253  		var wg sync.WaitGroup
   254  		cliConfig.NotifyOnConfigFileChange(func() {
   255  			wg.Done()
   256  		})
   257  		defer cliConfig.StopWatcher()
   258  		assert.True(t, cliConfig.watcherStarted)
   259  		time.Sleep(1 * time.Millisecond)
   260  		watcher.Errors <- expectedErr
   261  		time.Sleep(1 * time.Millisecond)
   262  		assert.Contains(t, buf.String(), expectedErr.Error())
   263  		assert.Contains(t, buf.String(), "watcher error")
   264  	})
   265  	t.Run("NoWatchDueToConfig", func(t *testing.T) {
   266  		inConfig := &CLIConfig{DoNotWatchConfigChange: true}
   267  		assert.True(t, inConfig.DoNotWatchConfigChange)
   268  		inConfig.NotifyOnConfigFileChange(func() {
   269  			t.FailNow()
   270  		})
   271  		assert.False(t, inConfig.IsConfigWatcherStarted())
   272  	})
   273  }