Files
HomeAgent/internal/supervisor/daemon_test.go
JianFeeeee 4e70b8d5d8 refactor(module): 核心仓 Go module 路径改为 github.com/JianFeeeee/HologramAgent
批 1,不含 SDK 仓与目录改名。

- go.mod 第 1 行 module 路径
- 366 个 .go 文件的完整前缀 github.com/JianFeeeee/HomeAgent 机械替换
  (857 处,仅完整前缀;展示名/注释里的 HomeAgent 字样留给批 5)
- 未改:third_party 目录名、SDK 自身 go.mod module、go.mod replace 目标、
  homed/waiter 可执行名、.hmap 后缀
2026-10-21 12:17:19 +08:00

271 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package supervisor
import (
"testing"
"time"
"github.com/JianFeeeee/HologramAgent/internal/tracker"
"github.com/JianFeeeee/HologramAgent/pkg/types"
)
func TestNew(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
if d == nil {
t.Fatal("Daemon should not be nil")
}
}
func TestStartAndShutdown(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
if err := d.Start(); err != nil {
t.Fatal(err)
}
d.Shutdown()
}
func TestRegisterAgent(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
d.RegisterAgent("test_agent")
agents := d.ListAgents()
if len(agents) != 1 {
t.Fatalf("expected 1 agent, got %d", len(agents))
}
if agents[0].ID != "test_agent" {
t.Errorf("expected 'test_agent', got %q", agents[0].ID)
}
if agents[0].State != types.AgentStateRunning {
t.Errorf("expected Running state, got %v", agents[0].State)
}
}
func TestGetAgentStatus(t *testing.T) {
cfg := &types.Config{
Defaults: types.AgentConfig{
RollbackPolicy: types.RollbackPolicy{MaxRetries: 5},
},
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
d.RegisterAgent("agent1")
status, err := d.GetAgentStatus("agent1")
if err != nil {
t.Fatal(err)
}
if status.ID != "agent1" {
t.Errorf("expected 'agent1', got %q", status.ID)
}
}
func TestGetAgentStatusNotFound(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
_, err := d.GetAgentStatus("nonexistent")
if err == nil {
t.Error("expected error for nonexistent agent")
}
}
func TestListAgents(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
d.RegisterAgent("a")
d.RegisterAgent("b")
agents := d.ListAgents()
if len(agents) != 2 {
t.Errorf("expected 2 agents, got %d", len(agents))
}
}
func TestSetTracker(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
tr := tracker.NewTracker("/tmp/test_tracker_data", "/tmp/test_tracker_work")
d.SetTracker(tr)
// RegisterAgent should use tracker
d.RegisterAgent("tracked_agent")
status, _ := d.GetAgentStatus("tracked_agent")
if status.TrackerStats == nil {
t.Error("expected tracker stats")
}
}
func TestRollbackAgent(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
tr := tracker.NewTracker("/tmp/test_rb_data", "/tmp/test_rb_work")
d.SetTracker(tr)
err := d.RollbackAgent("any", "snap1")
if err == nil {
t.Log("rollback succeeded (tracker may be unmounted)")
}
}
func TestRollbackAgentNoTracker(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
err := d.RollbackAgent("main", "snap1")
if err == nil {
t.Error("expected error when no tracker")
}
}
func TestPreActionSnapshot(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
_, err := d.PreActionSnapshot("main")
if err == nil {
t.Error("expected error (snapshot not supported)")
}
}
func TestRegisterAgentMultiple(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
for i := 0; i < 5; i++ {
d.RegisterAgent(types.AgentID(string(rune('a' + i))))
}
agents := d.ListAgents()
if len(agents) != 5 {
t.Errorf("expected 5 agents, got %d", len(agents))
}
}
func TestConcurrentAccess(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{
CheckInterval: time.Minute,
HeartbeatInterval: 30 * time.Second,
},
}
d := New(cfg)
// Register from multiple goroutines
for i := 0; i < 10; i++ {
go d.RegisterAgent(types.AgentID(string(rune('a' + i))))
}
}
func TestCheckAgentHeartbeatSource(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{CheckInterval: time.Minute, HeartbeatInterval: 30 * time.Second},
}
cfg.Defaults.RollbackPolicy.MaxRetries = 3
d := New(cfg)
d.RegisterAgent("main")
a := d.agents["main"]
d.SetHeartbeatSource(func(id types.AgentID) (time.Time, types.HealthStatus, error) {
return time.Now(), types.HealthHealthy, nil
})
regHB := a.lastHB
if regHB.IsZero() {
t.Fatal("lastHB should be set at register")
}
d.checkAgent("main", a)
if a.lastHB.Before(regHB) {
t.Fatal("lastHB should update after a successful heartbeat poll")
}
if a.health != types.HealthHealthy {
t.Fatalf("expected healthy, got %v", a.health)
}
// 健康源丢失:lastHB 不应再更新,状态置 Down
lastHB := a.lastHB
time.Sleep(2 * time.Millisecond)
d.SetHeartbeatSource(func(id types.AgentID) (time.Time, types.HealthStatus, error) {
return time.Time{}, types.HealthDown, nil
})
d.checkAgent("main", a)
if a.health != types.HealthDown {
t.Fatalf("expected down, got %v", a.health)
}
if !a.lastHB.Equal(lastHB) {
t.Fatal("lastHB must NOT update when the agent does not respond")
}
}
func TestRestartHandlerInvoked(t *testing.T) {
cfg := &types.Config{
Daemon: types.DaemonConfig{CheckInterval: time.Minute, HeartbeatInterval: 30 * time.Second},
}
cfg.Defaults.RollbackPolicy.MaxRetries = 3
d := New(cfg)
d.RegisterAgent("main")
a := d.agents["main"]
called := false
d.SetRestartHandler(func(id types.AgentID) { called = true })
d.restartAgent("main", a)
if !called {
t.Fatal("restart handler should be invoked")
}
if a.failCount != 0 {
t.Fatalf("failCount should reset, got %d", a.failCount)
}
}