Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • master
  • v1.0.0
  • v1.0.1
  • v1.1.0
  • v1.10.0
  • v1.11.0
  • v1.11.1
  • v1.11.2
  • v1.11.3
  • v1.11.4
  • v1.12.0
  • v1.13.0
  • v1.14.0
  • v1.15.0
  • v1.16.0
  • v1.17.1
  • v1.17.2
  • v1.17.3
  • v1.17.4
  • v1.17.5
  • v1.17.6
  • v1.17.7
  • v1.18.0
  • v1.18.1
  • v1.18.2
  • v1.18.3
  • v1.19.0
  • v1.2.0
  • v1.20.0
  • v1.20.1
  • v1.20.2
  • v1.20.3
  • v1.20.4
  • v1.20.5
  • v1.21.0
  • v1.22.0
  • v1.22.1
  • v1.22.3
  • v1.22.4
  • v1.22.5
  • v1.22.6
  • v1.22.7
  • v1.22.8
  • v1.22.9
  • v1.3.0
  • v1.4.0
  • v1.4.1
  • v1.4.2
  • v1.4.3
  • v1.5.0
  • v1.6.0
  • v1.7.0
  • v1.7.1
  • v1.8.0
  • v1.9.0
55 results

Target

Select target project
  • oss/libraries/go/application/configuration
1 result
Select Git revision
  • master
  • v1.0.0
  • v1.0.1
  • v1.1.0
  • v1.10.0
  • v1.11.0
  • v1.11.1
  • v1.11.2
  • v1.11.3
  • v1.11.4
  • v1.12.0
  • v1.13.0
  • v1.14.0
  • v1.15.0
  • v1.16.0
  • v1.17.1
  • v1.17.2
  • v1.17.3
  • v1.17.4
  • v1.17.5
  • v1.17.6
  • v1.17.7
  • v1.18.0
  • v1.18.1
  • v1.18.2
  • v1.18.3
  • v1.19.0
  • v1.2.0
  • v1.20.0
  • v1.20.1
  • v1.20.2
  • v1.20.3
  • v1.20.4
  • v1.20.5
  • v1.21.0
  • v1.22.0
  • v1.22.1
  • v1.22.3
  • v1.22.4
  • v1.22.5
  • v1.22.6
  • v1.22.7
  • v1.22.8
  • v1.22.9
  • v1.3.0
  • v1.4.0
  • v1.4.1
  • v1.4.2
  • v1.4.3
  • v1.5.0
  • v1.6.0
  • v1.7.0
  • v1.7.1
  • v1.8.0
  • v1.9.0
55 results
Show changes
Commits on Source (8)
<a name="v1.11.0"></a>
## [v1.11.0] - 2022-10-23
### Add Features
- feat error handler and changed update handler
- feat inform enventhandler about an error that has occurred
- feat new ChangeEventHandler struct
### Bug Fixes
- fix protect access to the config
### Changes
- chore licenses
### Code Refactoring
- refactor assign file list
<a name="v1.10.0"></a>
## [v1.10.0] - 2022-10-17
### Bug Fixes
......@@ -96,6 +113,7 @@
<a name="v1.0.0"></a>
## v1.0.0 - 2022-09-18
[v1.11.0]: https://gitlab.schukai.com/oss/libraries/go/application/configuration/compare/v1.10.0...v1.11.0
[v1.10.0]: https://gitlab.schukai.com/oss/libraries/go/application/configuration/compare/v1.9.0...v1.10.0
[v1.9.0]: https://gitlab.schukai.com/oss/libraries/go/application/configuration/compare/v1.8.0...v1.9.0
[v1.8.0]: https://gitlab.schukai.com/oss/libraries/go/application/configuration/compare/v1.7.1...v1.8.0
......
......@@ -3,13 +3,23 @@
package configuration
import "github.com/imdario/mergo"
import (
"github.com/imdario/mergo"
)
// NewSetting creates a new configuration setting
// with the given defaults.
func New[C any](defaults C) *Settings[C] {
s := &Settings[C]{}
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
s.initDefaults()
if err := mergo.Merge(&defaults, s.config); err != nil {
......@@ -32,6 +42,14 @@ func New[C any](defaults C) *Settings[C] {
// Set the mnemonic
// The mnemonic is used to identify the configuration in the configuration file
func (s *Settings[C]) SetMnemonic(mnemonic string) *Settings[C] {
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if mnemonic == "" {
s.errors = append(s.errors, MnemonicEmptyError)
} else {
......@@ -44,5 +62,9 @@ func (s *Settings[C]) SetMnemonic(mnemonic string) *Settings[C] {
// Remember that the configuration is a copy of the original configuration.
// Changes to the configuration will not be reflected in the original configuration.
func (s *Settings[C]) Config() C {
s.Lock()
defer s.Unlock()
return s.config
}
// Copyright 2022 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0
package configuration
import (
"github.com/r3labs/diff/v3"
)
type ChangeEvent struct {
Changlog diff.Changelog
}
type ChangeHook interface {
Handle(event ChangeEvent)
}
// OnChange registers a hook that is called when the configuration changes.
func (s *Settings[C]) OnChange(hook ChangeHook) *Settings[C] {
s.hooks.change = append(s.hooks.change, hook)
return s
}
// HasOnChangeHook returns true if there are registered hooks.
func (s *Settings[C]) HasOnChangeHook(hook ChangeHook) *Settings[C] {
for _, h := range s.hooks.change {
if h == hook {
break
}
}
return s
}
// RemoveOnChangeHook removes a change hook from the list of hooks.
func (s *Settings[C]) RemoveOnChangeHook(hook ChangeHook) *Settings[C] {
for i, h := range s.hooks.change {
if h == hook {
s.hooks.change = append(s.hooks.change[:i], s.hooks.change[i+1:]...)
break
}
}
return s
}
func (s *Settings[C]) notifyChangeHooks(changelog diff.Changelog) *Settings[C] {
for _, h := range s.hooks.change {
go h.Handle(ChangeEvent{Changlog: changelog})
}
return s
}
......@@ -7,48 +7,6 @@ import (
"github.com/r3labs/diff/v3"
)
type ChangeEvent struct {
Changlog diff.Changelog
}
type EventHook interface {
Handle(event ChangeEvent)
}
// OnChange registers a hook that is called when the configuration changes.
func (s *Settings[C]) OnChange(hook EventHook) *Settings[C] {
s.hooks.change = append(s.hooks.change, hook)
return s
}
// HasOnChangeHook returns true if there are registered hooks.
func (s *Settings[C]) HasOnChangeHook(hook EventHook) *Settings[C] {
for _, h := range s.hooks.change {
if h == hook {
break
}
}
return s
}
// RemoveOnChangeHook removes a change hook from the list of hooks.
func (s *Settings[C]) RemoveOnChangeHook(hook EventHook) *Settings[C] {
for i, h := range s.hooks.change {
if h == hook {
s.hooks.change = append(s.hooks.change[:i], s.hooks.change[i+1:]...)
break
}
}
return s
}
func (s *Settings[C]) notifyChangeHooks(changelog diff.Changelog) *Settings[C] {
for _, h := range s.hooks.change {
h.Handle(ChangeEvent{Changlog: changelog})
}
return s
}
func (s *Settings[C]) setConfigInternal(config C, lock bool) *Settings[C] {
var (
......@@ -64,13 +22,23 @@ func (s *Settings[C]) setConfigInternal(config C, lock bool) *Settings[C] {
if lock {
s.Unlock()
}
}()
defer func() {
if len(changelog) > 0 {
go s.notifyChangeHooks(changelog)
s.notifyChangeHooks(changelog)
}
}()
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if err := validateConfig[C](config); err != nil {
s.errors = append(s.errors, err)
return s
......
......@@ -12,7 +12,7 @@ import (
)
type mockTestEventHandler struct {
EventHook
ChangeHook
}
func (m *mockTestEventHandler) Handle(event ChangeEvent) {
......@@ -30,7 +30,7 @@ func TestAddRemoveHook(t *testing.T) {
s := New(config)
var h EventHook
var h ChangeHook
h = &mockTestEventHandler{}
s.OnChange(h)
......@@ -46,14 +46,6 @@ func TestAddRemoveHook(t *testing.T) {
}
type ChangeEventTester struct {
callback func(event ChangeEvent)
}
func (c *ChangeEventTester) Handle(event ChangeEvent) {
c.callback(event)
}
func TestReadmeExample(t *testing.T) {
config := struct {
......@@ -68,9 +60,9 @@ func TestReadmeExample(t *testing.T) {
msg := ""
var h EventHook
h = &ChangeEventTester{
callback: func(event ChangeEvent) {
var h ChangeHook
h = &ChangeEventHandler{
Callback: func(event ChangeEvent) {
log := event.Changlog
msg = fmt.Sprintf("Change from %s to %s", log[0].From, log[0].To)
// for Readme
......@@ -134,9 +126,9 @@ func TestCangeOnChange(t *testing.T) {
counter := 0
var h EventHook
h = &ChangeEventTester{
callback: func(event ChangeEvent) {
var h ChangeHook
h = &ChangeEventHandler{
Callback: func(event ChangeEvent) {
counter++
closeChan <- true
},
......
......@@ -12,6 +12,13 @@ func (s *Settings[C]) Copy(m map[string]any) {
c := s.Config()
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
for k, v := range m {
err := pathfinder.SetValue(&c, k, v)
if err != nil {
......
......@@ -14,6 +14,13 @@ func (s *Settings[C]) InitFromEnv(prefix string) *Settings[C] {
s.Lock()
defer s.Unlock()
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
err := runOnTags(&s.config, []string{envTagKey}, func(k string, field reflect.Value) {
if !field.CanSet() {
......
// Copyright 2022 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0
package configuration
type ErrorEvent struct {
}
type ErrorHook interface {
Handle(event ErrorEvent)
}
// OnChange registers a hook that is called when the configuration changes.
func (s *Settings[C]) OnError(hook ErrorHook) *Settings[C] {
s.hooks.error = append(s.hooks.error, hook)
return s
}
// HasOnChangeHook returns true if there are registered hooks.
func (s *Settings[C]) HasOnErrorHook(hook ErrorHook) *Settings[C] {
for _, h := range s.hooks.error {
if h == hook {
break
}
}
return s
}
// RemoveOnChangeHook removes a change hook from the list of hooks.
func (s *Settings[C]) RemoveOnErrorHook(hook ErrorHook) *Settings[C] {
for i, h := range s.hooks.error {
if h == hook {
s.hooks.error = append(s.hooks.error[:i], s.hooks.error[i+1:]...)
break
}
}
return s
}
func (s *Settings[C]) notifyErrorHooks() *Settings[C] {
for _, h := range s.hooks.error {
go h.Handle(ErrorEvent{})
}
return s
}
// Copyright 2022 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0
package configuration
import (
"github.com/stretchr/testify/assert"
"testing"
"time"
)
type mockTestEventErrorHandler struct {
ErrorHook
ch chan bool
}
func (m *mockTestEventErrorHandler) Handle(event ErrorEvent) {
// do nothing
m.ch <- true
}
func TestErrorHandling(t *testing.T) {
defaults := ConfigStruct2{
A: "Hello!",
}
c := New(defaults)
var h ErrorHook
h = &mockTestEventErrorHandler{
ch: make(chan bool),
}
c.OnError(h)
c.SetDefaultDirectories()
select {
case <-h.(*mockTestEventErrorHandler).ch:
assert.True(t, true)
case <-time.After(11 * time.Second):
t.Error("Timeout, expected error event")
}
}
func TestAddRemoveErrorHook(t *testing.T) {
config := struct {
Host string
}{
Host: "localhost",
}
s := New(config)
var h ErrorHook
h = &mockTestEventErrorHandler{}
s.OnError(h)
if len(s.hooks.error) != 1 {
t.Error("Expected 1 got ", len(s.hooks.change))
}
s.RemoveOnErrorHook(h)
if len(s.hooks.error) != 0 {
t.Error("Expected 0 got ", len(s.hooks.change))
}
}
......@@ -34,6 +34,13 @@ func (s *Settings[C]) writeProperties(writer io.Writer) error {
m, errors := getMapForProperties[C](s.config)
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if len(errors) > 0 {
for _, err := range errors {
s.errors = append(s.errors, err)
......@@ -43,6 +50,7 @@ func (s *Settings[C]) writeProperties(writer io.Writer) error {
p := properties.LoadMap(m)
_, err := p.Write(writer, properties.UTF8)
return err
}
......@@ -53,6 +61,13 @@ func (s *Settings[C]) WriteFile(fn string, format Format) *Settings[C] {
var file *os.File
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if fn == "" {
file = os.Stdout
} else {
......@@ -65,6 +80,7 @@ func (s *Settings[C]) WriteFile(fn string, format Format) *Settings[C] {
defer file.Close()
s.Write(io.Writer(file), format)
return s
}
......@@ -73,6 +89,13 @@ func (s *Settings[C]) Write(writer io.Writer, format Format) *Settings[C] {
var err error
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
switch format {
case Json:
err = s.writeJson(writer)
......
......@@ -38,6 +38,13 @@ func (s *Settings[C]) AddFile(file string, format ...Format) *Settings[C] {
var f Format
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if format == nil || len(format) == 0 {
f = RecogniseFormat
} else if format != nil && len(format) == 1 {
......@@ -104,6 +111,13 @@ func (s *Settings[C]) AddDirectory(d string) *Settings[C] {
func (s *Settings[C]) sanitizeDirectories() {
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
wd, err := os.Getwd()
if err != nil {
s.errors = append(s.errors, err)
......@@ -146,9 +160,17 @@ func (s *Settings[C]) AddWorkingDirectory() *Settings[C] {
s.Lock()
defer s.Unlock()
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
current, err := os.Getwd()
if err != nil {
s.errors = append(s.errors, err)
return s
}
s.files.directories = append(s.files.directories, current)
......@@ -161,6 +183,13 @@ func (s *Settings[C]) AddEtcDirectory() *Settings[C] {
s.Lock()
defer s.Unlock()
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if s.mnemonic == "" {
s.errors = append(s.errors, MnemonicEmptyError)
return s
......@@ -183,14 +212,21 @@ func (s *Settings[C]) AddEtcDirectory() *Settings[C] {
// The mnemonic must be set for this function
func (s *Settings[C]) AddUserConfigDirectory() *Settings[C] {
s.Lock()
defer s.Unlock()
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if s.mnemonic == "" {
s.errors = append(s.errors, MnemonicEmptyError)
return s
}
s.Lock()
defer s.Unlock()
current, err := os.UserConfigDir()
if err != nil {
s.errors = append(s.errors, err)
......@@ -214,6 +250,13 @@ func (s *Settings[C]) SetDefaultDirectories() *Settings[C] {
func (s *Settings[C]) SetFileFormat(format Format) *Settings[C] {
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if slices.Contains(availableFormats, format) {
s.files.format = format
} else {
......@@ -226,6 +269,13 @@ func (s *Settings[C]) SetFileFormat(format Format) *Settings[C] {
// Set the file name without extension
func (s *Settings[C]) SetFileName(name string) *Settings[C] {
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if name == "" {
s.errors = append(s.errors, FileNameEmptyError)
} else {
......
......@@ -13,6 +13,13 @@ import (
// The file is read from the flag specified by the name
func (s *Settings[C]) AddFileFromFlagSet(flagset *flag.FlagSet, name string, format Format) *Settings[C] {
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
flag := flagset.Lookup(name)
if flag == nil {
s.errors = append(s.errors, newFlagDoesNotExistError(name))
......@@ -33,6 +40,13 @@ func (s *Settings[C]) InitFromFlagSet(flagset *flag.FlagSet) *Settings[C] {
s.Lock()
defer s.Unlock()
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
err := runOnTags(&s.config, []string{flagTagKey}, func(k string, field reflect.Value) {
flag := flagset.Lookup(k)
......
// Copyright 2022 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0
package configuration
type ChangeEventHandler struct {
Callback func(event ChangeEvent)
}
func (c *ChangeEventHandler) Handle(event ChangeEvent) {
c.Callback(event)
}
type ErrorEventHandler struct {
Callback func(event ErrorEvent)
}
func (c *ErrorEventHandler) Handle(event ErrorEvent) {
c.Callback(event)
}
......@@ -272,9 +272,9 @@ func TestConfigurationServePostJson(t *testing.T) {
closeChan := make(chan bool)
counter := 0
var h EventHook
h = &ChangeEventTester{
callback: func(event ChangeEvent) {
var h ChangeHook
h = &ChangeEventHandler{
Callback: func(event ChangeEvent) {
counter++
closeChan <- true
},
......
......@@ -6,6 +6,7 @@ package configuration
import (
"bytes"
"encoding/json"
"github.com/imdario/mergo"
"github.com/magiconair/properties"
"github.com/pelletier/go-toml/v2"
......@@ -49,6 +50,13 @@ func (s *Settings[C]) importStream(r reader) {
var c C
var err error
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
err = nil // reset error
x := r.reader
......@@ -85,10 +93,21 @@ func (s *Settings[C]) importStreams() {
func (s *Settings[C]) importFiles() {
s.fileWatch.Lock()
defer s.fileWatch.Unlock()
// new files may have been added
s.fileWatch.watchList = make(map[string]string)
tmpWatchList := make(map[string]string)
defer func() {
s.fileWatch.watchList = tmpWatchList
s.fileWatch.Unlock()
}()
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
for _, d := range s.files.directories {
fn := path.Join(d, s.files.name+s.files.format.Extension())
......@@ -104,7 +123,7 @@ func (s *Settings[C]) importFiles() {
s.importStream(reader{s.files.format, r})
f.Close()
s.fileWatch.watchList[fn] = fn
tmpWatchList[fn] = fn
}
......@@ -117,8 +136,9 @@ func (s *Settings[C]) importFiles() {
continue
}
s.importStream(reader{f.format, r})
r.Close()
s.fileWatch.watchList[f.path] = f.path
tmpWatchList[f.path] = f.path
}
}
......@@ -128,6 +148,13 @@ func (s *Settings[C]) Import() *Settings[C] {
s.Lock()
defer s.Unlock()
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
defaults := s.config
var n C
......
{"version":"1.10.0"}
{"version":"1.11.0"}
......@@ -28,7 +28,8 @@ type Settings[C any] struct {
mnemonic string
importCounter int
hooks struct {
change []EventHook
change []ChangeHook
error []ErrorHook
}
fileWatch fileWatch
......@@ -36,6 +37,13 @@ type Settings[C any] struct {
func (s *Settings[C]) initDefaults() *Settings[C] {
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
err := runOnTags(&s.config, []string{"default"}, func(v string, field reflect.Value) {
if field.CanSet() {
......
......@@ -11,6 +11,13 @@ func (s *Settings[C]) initWatch() *Settings[C] {
var err error
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if s.fileWatch.watcher != nil {
s.errors = append(s.errors, WatchAlreadyInitializedError)
return s
......@@ -31,6 +38,13 @@ func (s *Settings[C]) StopWatching() *Settings[C] {
s.fileWatch.Lock()
defer s.fileWatch.Unlock()
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if s.fileWatch.watcher == nil {
s.errors = append(s.errors, WatchNotInitializedError)
return s
......@@ -52,6 +66,13 @@ func (s *Settings[C]) Watch() *Settings[C] {
s.fileWatch.Lock()
defer s.fileWatch.Unlock()
errorCount := len(s.errors)
defer func() {
if len(s.errors) > errorCount {
s.notifyErrorHooks()
}
}()
if s.fileWatch.watcher == nil {
s.initWatch()
}
......
......@@ -10,6 +10,135 @@ import (
"time"
)
func runTestFilesChange(fn string, data []testHostTimeout, t *testing.T) {
for _, x := range data {
h := x.host
b := []byte("Host: \"" + h + "\"")
err := os.WriteFile(fn, b, 0644)
if err != nil {
t.Error(err)
return
}
time.Sleep(x.timeout)
}
}
func createTestFileForWatch1() (string, error) {
f, err := os.CreateTemp("", "watch_test")
if err != nil {
return "", err
}
f.WriteString("Host: \"127.0.0.1\"")
f.Close()
return f.Name(), nil
}
type testHostTimeout struct {
host string
timeout time.Duration
}
func TestMultiChange(t *testing.T) {
tmpFn, err := createTestFileForWatch1()
if err != nil {
t.Error(err)
return
}
defer os.Remove(tmpFn)
config := struct {
Host string `yaml:"Host"`
}{
Host: "localhost",
}
c := New(config)
c.SetMnemonic("my-app")
assert.Equal(t, c.Config().Host, "localhost")
result := []string{}
signal := make(chan string)
var h ChangeHook
h = &ChangeEventHandler{
Callback: func(event ChangeEvent) {
result = append(result, event.Changlog[0].To.(string))
signal <- event.Changlog[0].To.(string)
},
}
var e ErrorHook
e = &ErrorEventHandler{
Callback: func(event ErrorEvent) {
//for _, err := range c.Errors() {
// t.Error(err)
//}
},
}
c.AddFile(tmpFn, Yaml)
c.Import()
if c.HasErrors() {
t.Error(c.Errors())
}
data := []testHostTimeout{
{
host: "1.org",
timeout: time.Millisecond * 100,
},
{
host: "2.org",
timeout: time.Millisecond * 10,
},
{
host: "3.org",
timeout: time.Millisecond * 2,
},
{
host: "4.org",
timeout: time.Millisecond * 100,
},
{
host: "9.org",
timeout: time.Millisecond * 100,
},
}
c.OnChange(h).OnError(e).Watch()
go runTestFilesChange(tmpFn, data, t)
for loop := true; loop; {
select {
case <-signal:
if len(result) == len(data) {
loop = false
break
}
case <-time.After(time.Second * 10):
t.Log(result)
t.Fatalf("Timeout")
}
}
assert.Equal(t, "9.org", c.Config().Host)
}
func TestWatch(t *testing.T) {
f, err := os.CreateTemp("", "watch_test")
......@@ -35,9 +164,9 @@ func TestWatch(t *testing.T) {
signal := make(chan bool)
var h EventHook
h = &ChangeEventTester{
callback: func(event ChangeEvent) {
var h ChangeHook
h = &ChangeEventHandler{
Callback: func(event ChangeEvent) {
assert.Equal(t, event.Changlog[0].From, "localhost")
assert.Equal(t, event.Changlog[0].To, "example.org")
signal <- true
......