package file import ( "errors" "os" "path/filepath" "reflect" "sync" "testing" "time" "github.com/go-kratos/kratos/v2/config" ) const ( _testJSON = ` { "test":{ "settings":{ "int_key":1000, "float_key":1000.1, "duration_key":10000, "string_key":"string_value" }, "server":{ "addr":"127.0.0.1", "port":8000 } }, "foo":[ { "name":"nihao", "age":18 }, { "name":"nihao", "age":18 } ] }` _testJSONUpdate = ` { "test":{ "settings":{ "int_key":1000, "float_key":1000.1, "duration_key":10000, "string_key":"string_value" }, "server":{ "addr":"127.0.0.1", "port":8000 } }, "foo":[ { "name":"nihao", "age":18 }, { "name":"nihao", "age":18 } ], "bar":{ "event":"update" } }` // _testYaml = ` //Foo: // bar : // - {name: nihao,age: 1} // - {name: nihao,age: 1} // // //` ) //func TestScan(t *testing.T) { // //} func TestFile(t *testing.T) { var ( path = filepath.Join(t.TempDir(), "test_config") file = filepath.Join(path, "test.json") data = []byte(_testJSON) ) defer os.Remove(path) if err := os.MkdirAll(path, 0o700); err != nil { t.Error(err) } if err := os.WriteFile(file, data, 0o666); err != nil { t.Error(err) } testSource(t, file, data) testSource(t, path, data) testWatchFile(t, file) testWatchDir(t, path, file) } func testWatchFile(t *testing.T, path string) { t.Log(path) s := NewSource(path) watch, err := s.Watch() if err != nil { t.Error(err) } f, err := os.OpenFile(path, os.O_RDWR, 0) if err != nil { t.Error(err) } defer f.Close() _, err = f.WriteString(_testJSONUpdate) if err != nil { t.Error(err) } kvs, err := watch.Next() if err != nil { t.Errorf(`watch.Next() error(%v)`, err) } if !reflect.DeepEqual(string(kvs[0].Value), _testJSONUpdate) { t.Errorf(`string(kvs[0].Value(%v) is not equal to _testJSONUpdate(%v)`, kvs[0].Value, _testJSONUpdate) } newFilepath := filepath.Join(filepath.Dir(path), "test1.json") if err = os.Rename(path, newFilepath); err != nil { t.Error(err) } kvs, err = watch.Next() if err == nil { t.Errorf(`watch.Next() error(%v)`, err) } if kvs != nil { t.Errorf(`watch.Next() error(%v)`, err) } err = watch.Stop() if err != nil { t.Errorf(`watch.Stop() error(%v)`, err) } if err := os.Rename(newFilepath, path); err != nil { t.Error(err) } } func testWatchDir(t *testing.T, path, file string) { t.Log(path) t.Log(file) s := NewSource(path) watch, err := s.Watch() if err != nil { t.Error(err) } f, err := os.OpenFile(file, os.O_RDWR, 0) if err != nil { t.Error(err) } defer f.Close() _, err = f.WriteString(_testJSONUpdate) if err != nil { t.Error(err) } kvs, err := watch.Next() if err != nil { t.Errorf(`watch.Next() error(%v)`, err) } if !reflect.DeepEqual(string(kvs[0].Value), _testJSONUpdate) { t.Errorf(`string(kvs[0].Value(%v) is not equal to _testJSONUpdate(%v)`, kvs[0].Value, _testJSONUpdate) } } func testSource(t *testing.T, path string, data []byte) { t.Log(path) s := NewSource(path) kvs, err := s.Load() if err != nil { t.Error(err) } if string(kvs[0].Value) != string(data) { t.Errorf("no expected: %s, but got: %s", kvs[0].Value, data) } } func TestConfig(t *testing.T) { path := filepath.Join(t.TempDir(), "test_config.json") defer os.Remove(path) if err := os.WriteFile(path, []byte(_testJSON), 0o666); err != nil { t.Error(err) } c := config.New(config.WithSource( NewSource(path), )) testScan(t, c) testConfig(t, c) } func testConfig(t *testing.T, c config.Config) { expected := map[string]interface{}{ "test.settings.int_key": int64(1000), "test.settings.float_key": 1000.1, "test.settings.string_key": "string_value", "test.settings.duration_key": time.Duration(10000), "test.server.addr": "127.0.0.1", "test.server.port": int64(8000), } if err := c.Load(); err != nil { t.Error(err) } for key, value := range expected { switch value.(type) { case int64: if v, err := c.Value(key).Int(); err != nil { t.Error(key, value, err) } else if v != value { t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v) } case float64: if v, err := c.Value(key).Float(); err != nil { t.Error(key, value, err) } else if v != value { t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v) } case string: if v, err := c.Value(key).String(); err != nil { t.Error(key, value, err) } else if v != value { t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v) } case time.Duration: if v, err := c.Value(key).Duration(); err != nil { t.Error(key, value, err) } else if v != value { t.Errorf("no expect key: %s value: %v, but got: %v", key, value, v) } } } // scan var settings struct { IntKey int64 `json:"int_key"` FloatKey float64 `json:"float_key"` StringKey string `json:"string_key"` DurationKey time.Duration `json:"duration_key"` } if err := c.Value("test.settings").Scan(&settings); err != nil { t.Error(err) } if v := expected["test.settings.int_key"]; settings.IntKey != v { t.Errorf("no expect int_key value: %v, but got: %v", settings.IntKey, v) } if v := expected["test.settings.float_key"]; settings.FloatKey != v { t.Errorf("no expect float_key value: %v, but got: %v", settings.FloatKey, v) } if v := expected["test.settings.string_key"]; settings.StringKey != v { t.Errorf("no expect string_key value: %v, but got: %v", settings.StringKey, v) } if v := expected["test.settings.duration_key"]; settings.DurationKey != v { t.Errorf("no expect duration_key value: %v, but got: %v", settings.DurationKey, v) } // not found if _, err := c.Value("not_found_key").Bool(); errors.Is(err, config.ErrNotFound) { t.Logf("not_found_key not match: %v", err) } } func testScan(t *testing.T, c config.Config) { type TestJSON struct { Test struct { Settings struct { IntKey int `json:"int_key"` FloatKey float64 `json:"float_key"` DurationKey int `json:"duration_key"` StringKey string `json:"string_key"` } `json:"settings"` Server struct { Addr string `json:"addr"` Port int `json:"port"` } `json:"server"` } `json:"test"` Foo []struct { Name string `json:"name"` Age int `json:"age"` } `json:"foo"` } var conf TestJSON if err := c.Load(); err != nil { t.Error(err) } if err := c.Scan(&conf); err != nil { t.Error(err) } t.Log(conf) } func TestMergeDataRace(t *testing.T) { path := filepath.Join(t.TempDir(), "test_config.json") defer os.Remove(path) if err := os.WriteFile(path, []byte(_testJSON), 0o666); err != nil { t.Error(err) } c := config.New(config.WithSource( NewSource(path), )) const count = 80 wg := &sync.WaitGroup{} wg.Add(2) startCh := make(chan struct{}) go func() { defer wg.Done() <-startCh for i := 0; i < count; i++ { var conf struct{} if err := c.Scan(&conf); err != nil { t.Error(err) } } }() go func() { defer wg.Done() <-startCh for i := 0; i < count; i++ { if err := c.Load(); err != nil { t.Error(err) } } }() close(startCh) wg.Wait() }