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 }