package grpc import ( "testing" "time" agentv1 "github.com/containarr/server/internal/proto/agentv1" ) func TestRegisterAndGet(t *testing.T) { r := NewRegistry() state := r.Register("id1", "hostname1", "alias1", "10.0.0.1", "amd64", "linux") if state == nil { t.Fatal("Register returned nil") } got, ok := r.Get("id1") if !ok { t.Fatal("Get returned false for registered agent") } if got.ID != "id1" || got.Hostname != "hostname1" || got.Alias != "alias1" { t.Errorf("unexpected state: %+v", got) } } func TestGet_NotFound(t *testing.T) { r := NewRegistry() _, ok := r.Get("nonexistent") if ok { t.Error("expected false for unknown agent") } } func TestDeregister(t *testing.T) { r := NewRegistry() r.Register("id1", "h", "a", "ip", "arch", "os") r.Deregister("id1") _, ok := r.Get("id1") if ok { t.Error("agent should not exist after Deregister") } } func TestDeregister_NotExist(t *testing.T) { r := NewRegistry() // must not panic r.Deregister("ghost") } func TestList(t *testing.T) { r := NewRegistry() if len(r.List()) != 0 { t.Error("expected empty list") } r.Register("a1", "h1", "", "", "", "") r.Register("a2", "h2", "", "", "", "") if len(r.List()) != 2 { t.Errorf("expected 2 agents, got %d", len(r.List())) } } func TestUpdateContainers(t *testing.T) { r := NewRegistry() r.Register("id1", "h", "a", "ip", "arch", "os") before := time.Now() containers := []*agentv1.ContainerInfo{ {Id: "c1", Name: "web"}, {Id: "c2", Name: "db"}, } r.UpdateContainers("id1", containers) got, _ := r.Get("id1") if len(got.Containers) != 2 { t.Errorf("expected 2 containers, got %d", len(got.Containers)) } if got.LastSeenAt.Before(before) { t.Error("LastSeenAt should have been updated") } } func TestUpdateContainers_UnknownAgent(t *testing.T) { r := NewRegistry() // must not panic r.UpdateContainers("ghost", nil) } func TestUpdateAlias(t *testing.T) { r := NewRegistry() r.Register("id1", "h", "old-alias", "ip", "arch", "os") r.UpdateAlias("id1", "new-alias") got, _ := r.Get("id1") if got.Alias != "new-alias" { t.Errorf("expected 'new-alias', got %q", got.Alias) } } func TestUpdateAlias_UnknownAgent(t *testing.T) { r := NewRegistry() // must not panic r.UpdateAlias("ghost", "alias") } func TestSend(t *testing.T) { r := NewRegistry() state := r.Register("id1", "h", "a", "ip", "arch", "os") msg := &agentv1.ServerMessage{} ok := r.Send("id1", msg) if !ok { t.Fatal("Send returned false for connected agent") } // Drain the channel to verify the message arrived. select { case got := <-state.cmdCh: if got != msg { t.Error("received wrong message") } case <-time.After(time.Second): t.Fatal("timed out reading from cmdCh") } } func TestSend_UnknownAgent(t *testing.T) { r := NewRegistry() ok := r.Send("ghost", &agentv1.ServerMessage{}) if ok { t.Error("Send should return false for unknown agent") } } func TestSend_FullChannel(t *testing.T) { r := NewRegistry() r.Register("id1", "h", "a", "ip", "arch", "os") // Fill the buffer (size 16) for i := 0; i < 16; i++ { r.Send("id1", &agentv1.ServerMessage{}) } // Next send on a full channel should return false ok := r.Send("id1", &agentv1.ServerMessage{}) if ok { t.Error("Send should return false when channel is full") } }