package agent import ( "errors" "os" "path/filepath" "sync/atomic" "testing" "time" llmgwv1 "gitea.mynodes.xyz/ilker/llmgw/api/gen/llmgw/v1" ) func settingsAgent(t *testing.T, running string) (*Agent, chan struct{}) { t.Helper() oldFile, oldExit := SettingsFile, exitForUpgrade SettingsFile = filepath.Join(t.TempDir(), "agent-settings.yaml") exited := make(chan struct{}, 1) exitForUpgrade = func() { exited <- struct{}{} } t.Cleanup(func() { SettingsFile, exitForUpgrade = oldFile, oldExit }) cfg := Config{Server: "127.0.0.1:7443", NodeID: "n1", WeightsCache: t.TempDir(), EnginesDir: t.TempDir(), Settings: running} cfg.Superv.WorkDir = t.TempDir() a, err := New(cfg, nil, nil) if err != nil { t.Fatal(err) } return a, exited } // exitedSoon is whether the agent exited within a second: it waits a // moment for its log line to leave first. func exitedSoon(ch chan struct{}) bool { select { case <-ch: return true case <-time.After(time.Second): return false } } func TestAnAgentOnItsSettingsStaysAndOneOffThemStartsAgain(t *testing.T) { a, exited := settingsAgent(t, "heartbeat_interval: 5s\n") a.applySettings(&llmgwv1.AgentSettings{Yaml: "heartbeat_interval: 5s\n"}) if exitedSoon(exited) || ReadSettings() != nil { t.Error("settings it already runs on restarted it") } a.applySettings(&llmgwv1.AgentSettings{Yaml: "heartbeat_interval: 2s\n"}) if !exitedSoon(exited) || string(ReadSettings()) != "heartbeat_interval: 2s\n" { t.Errorf("new settings: kept %q", ReadSettings()) } } func TestSettingsThatWouldNotLoadAreNotTaken(t *testing.T) { a, exited := settingsAgent(t, "") a.SetSettingsCheck(func([]byte) error { return errors.New("fabric ib-a lists no HCAs") }) a.applySettings(&llmgwv1.AgentSettings{Yaml: "fabrics: [{name: ib-a}]\n"}) if exitedSoon(exited) || ReadSettings() != nil { t.Error("settings that do not load were taken") } } func TestAChangedAgentYamlThatLoadsStartsTheAgentAgain(t *testing.T) { a, exited := settingsAgent(t, "") old := watchEvery watchEvery = 10 * time.Millisecond t.Cleanup(func() { watchEvery = old }) p := filepath.Join(t.TempDir(), "agent.yaml") _ = os.WriteFile(p, []byte("node_id: n1\n"), 0o600) var loads atomic.Bool check := func() error { if loads.Load() { return nil } return errors.New("broken") } go a.WatchFile(t.Context(), p, check) time.Sleep(30 * time.Millisecond) // Saved broken: reported, not restarted into. _ = os.WriteFile(p, []byte("node_id: [\n"), 0o600) if exitedSoon(exited) { t.Fatal("a broken agent.yaml restarted the agent") } loads.Store(true) _ = os.WriteFile(p, []byte("node_id: n1\nlabels: {a: b}\n"), 0o600) if !exitedSoon(exited) { t.Error("a changed agent.yaml did not restart the agent") } }