feat(node): implement flap tolerance settings and options dialog in frontend
This commit is contained in:
1 parent
9137d53286
commit
1a7a4cdc07
5 files changed
+251
-26
No files matched your search
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -61,3 +62,79 @@ func TestNodeFlapTolerantWithoutConfig(t *testing.T) {
|
||||
t.Errorf("node flap tolerance from an empty file = %v, want 0", got)
|
||||
}
|
||||
}
|
||||
|
||||
// nodeEntry reads one node's raw object back from the settings file, the way
|
||||
// GetSettings serves it to the console.
|
||||
func nodeEntry(t *testing.T, uuid string) map[string]interface{} {
|
||||
t.Helper()
|
||||
data, err := GetSettings(SettingBotNodeConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("GetSettings: %v", err)
|
||||
}
|
||||
var all map[string]map[string]interface{}
|
||||
if err := json.Unmarshal(data, &all); err != nil {
|
||||
t.Fatalf("unmarshal settings: %v", err)
|
||||
}
|
||||
return all[uuid]
|
||||
}
|
||||
|
||||
// TestFlapTolerantSettingsRoundTrip drives the payload the console's node card
|
||||
// actually posts — { "<uuid>": { "flap_tolerant": <seconds> } } — through the
|
||||
// settings API, so the frontend and the accessor are checked against each other
|
||||
// and not just against hand-written files.
|
||||
func TestFlapTolerantSettingsRoundTrip(t *testing.T) {
|
||||
writeTempConfig(t, &global.ConfigPath.BotNodeConfig, `{
|
||||
"node-a": { "enableStatusNotify": false }
|
||||
}`)
|
||||
if err := LoadBotNodeConfig(global.ConfigPath.BotNodeConfig); err != nil {
|
||||
t.Fatalf("LoadBotNodeConfig: %v", err)
|
||||
}
|
||||
|
||||
patch := map[string]interface{}{
|
||||
"node-a": map[string]interface{}{"flap_tolerant": 30},
|
||||
}
|
||||
if err := UpdateSettings(SettingBotNodeConfig, patch); err != nil {
|
||||
t.Fatalf("UpdateSettings: %v", err)
|
||||
}
|
||||
|
||||
if got := NodeFlapTolerant("node-a"); got != 30*time.Second {
|
||||
t.Errorf("NodeFlapTolerant after save = %v, want 30s", got)
|
||||
}
|
||||
// The sibling option must survive: the card saves only the field it edits.
|
||||
entry := nodeEntry(t, "node-a")
|
||||
if entry["enableStatusNotify"] != false {
|
||||
t.Errorf("enableStatusNotify = %v, want it preserved as false", entry["enableStatusNotify"])
|
||||
}
|
||||
if entry["flap_tolerant"] != float64(30) {
|
||||
t.Errorf("flap_tolerant on disk = %v, want 30", entry["flap_tolerant"])
|
||||
}
|
||||
}
|
||||
|
||||
// TestFlapTolerantSettingsRemoval: clearing the console's field sends a JSON
|
||||
// null, which must remove the key rather than leave an explicit 0 behind.
|
||||
func TestFlapTolerantSettingsRemoval(t *testing.T) {
|
||||
writeTempConfig(t, &global.ConfigPath.BotNodeConfig, `{
|
||||
"node-a": { "enableStatusNotify": true, "flap_tolerant": 30 }
|
||||
}`)
|
||||
if err := LoadBotNodeConfig(global.ConfigPath.BotNodeConfig); err != nil {
|
||||
t.Fatalf("LoadBotNodeConfig: %v", err)
|
||||
}
|
||||
|
||||
patch := map[string]interface{}{
|
||||
"node-a": map[string]interface{}{"flap_tolerant": nil},
|
||||
}
|
||||
if err := UpdateSettings(SettingBotNodeConfig, patch); err != nil {
|
||||
t.Fatalf("UpdateSettings: %v", err)
|
||||
}
|
||||
|
||||
if got := NodeFlapTolerant("node-a"); got != 0 {
|
||||
t.Errorf("NodeFlapTolerant after removal = %v, want 0", got)
|
||||
}
|
||||
entry := nodeEntry(t, "node-a")
|
||||
if _, present := entry["flap_tolerant"]; present {
|
||||
t.Error("flap_tolerant is still in the file after being cleared")
|
||||
}
|
||||
if entry["enableStatusNotify"] != true {
|
||||
t.Errorf("enableStatusNotify = %v, want it preserved as true", entry["enableStatusNotify"])
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user