package gateway import ( "context" "testing" "code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/handler" "code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/models" ) func TestInitialSync(t *testing.T) { _ = &mockAgentRepo{agents: make(map[string]models.AgentCardData)} // verify mock compiles broker := handler.NewBroker() capture := newBroadcastCapture(broker) defer capture.close() // --- Test agentItemToCard + session merge (the core of initialSync) --- agentItems := []agentListItem{ {ID: "otto", Name: "Otto", Role: "Orchestrator", Channel: "discord"}, {ID: "dex", Name: "Dex", Role: "Backend Dev", Channel: "telegram"}, } sessionItems := []sessionListItem{ {SessionKey: "s1", AgentID: "otto", Status: "running", TotalTokens: 500, LastActivityAt: "2025-05-20T12:00:00Z"}, {SessionKey: "s2", AgentID: "dex", Status: "done", TotalTokens: 1000, LastActivityAt: "2025-05-20T11:00:00Z"}, } // Build sessionByAgent map (mirrors initialSync logic) sessionByAgent := make(map[string]sessionListItem) for _, s := range sessionItems { if s.AgentID != "" { sessionByAgent[s.AgentID] = s } } // Merge and verify merged := make([]models.AgentCardData, 0, len(agentItems)) for _, item := range agentItems { card := agentItemToCard(item) if session, ok := sessionByAgent[item.ID]; ok { card.SessionKey = session.SessionKey card.Status = mapSessionStatus(session.Status) card.LastActivity = session.LastActivityAt if session.TotalTokens > 0 { prog := min(session.TotalTokens/100, 100) card.TaskProgress = &prog } } merged = append(merged, card) } // Verify otto: running → active if merged[0].ID != "otto" { t.Errorf("merged[0].ID = %q, want %q", merged[0].ID, "otto") } if merged[0].Status != models.AgentStatusActive { t.Errorf("otto status = %q, want %q (running → active)", merged[0].Status, models.AgentStatusActive) } if merged[0].SessionKey != "s1" { t.Errorf("otto sessionKey = %q, want %q", merged[0].SessionKey, "s1") } if merged[0].TaskProgress == nil || *merged[0].TaskProgress != 5 { t.Errorf("otto taskProgress = %v, want 5", merged[0].TaskProgress) } // Verify dex: done → idle if merged[1].ID != "dex" { t.Errorf("merged[1].ID = %q, want %q", merged[1].ID, "dex") } if merged[1].Status != models.AgentStatusIdle { t.Errorf("dex status = %q, want %q (done → idle)", merged[1].Status, models.AgentStatusIdle) } if merged[1].SessionKey != "s2" { t.Errorf("dex sessionKey = %q, want %q", merged[1].SessionKey, "s2") } } func TestInitialSync_PersistCreatesNew(t *testing.T) { repo := &mockAgentRepo{agents: make(map[string]models.AgentCardData)} broker := handler.NewBroker() capture := newBroadcastCapture(broker) defer capture.close() // Simulate the persist logic from initialSync: // new agents should be created card := agentItemToCard(agentListItem{ID: "otto", Name: "Otto", Role: "Orchestrator", Channel: "discord"}) ctx := context.Background() // Agent doesn't exist → create _, err := repo.Get(ctx, card.ID) if err == nil { t.Fatal("expected agent to not exist yet") } if err := repo.Create(ctx, card); err != nil { t.Fatalf("Create failed: %v", err) } got, err := repo.Get(ctx, card.ID) if err != nil { t.Fatalf("Get after Create failed: %v", err) } if got.ID != "otto" { t.Errorf("got.ID = %q, want %q", got.ID, "otto") } if got.DisplayName != "Otto" { t.Errorf("got.DisplayName = %q, want %q", got.DisplayName, "Otto") } if got.Role != "Orchestrator" { t.Errorf("got.Role = %q, want %q", got.Role, "Orchestrator") } } func TestInitialSync_PersistUpdatesExisting(t *testing.T) { repo := &mockAgentRepo{agents: make(map[string]models.AgentCardData)} broker := handler.NewBroker() capture := newBroadcastCapture(broker) defer capture.close() ctx := context.Background() // Pre-populate with existing agent repo.agents["otto"] = models.AgentCardData{ ID: "otto", DisplayName: "Otto", Role: "Old Role", Status: models.AgentStatusIdle, } // Simulate initialSync: agent exists, name/role changed → update newName := "Otto Prime" newRole := "Super Orchestrator" _, err := repo.Update(ctx, "otto", models.UpdateAgentRequest{ DisplayName: &newName, Role: &newRole, }) if err != nil { t.Fatalf("Update failed: %v", err) } got, err := repo.Get(ctx, "otto") if err != nil { t.Fatalf("Get after Update failed: %v", err) } if got.DisplayName != "Otto Prime" { t.Errorf("displayName = %q, want %q", got.DisplayName, "Otto Prime") } if got.Role != "Super Orchestrator" { t.Errorf("role = %q, want %q", got.Role, "Super Orchestrator") } } func TestInitialSync_MergesSessionStatus(t *testing.T) { // When initialSync merges session state, an agent whose existing status // differs from the session-derived status should be updated. repo := &mockAgentRepo{agents: make(map[string]models.AgentCardData)} ctx := context.Background() repo.agents["otto"] = models.AgentCardData{ ID: "otto", DisplayName: "Otto", Role: "Orchestrator", Status: models.AgentStatusIdle, } // Simulate session merge: session says "running" → agent should go active activeStatus := mapSessionStatus("running") if activeStatus != models.AgentStatusActive { t.Fatalf("mapSessionStatus(running) = %q, want active", activeStatus) } _, err := repo.Update(ctx, "otto", models.UpdateAgentRequest{ Status: &activeStatus, }) if err != nil { t.Fatalf("Update failed: %v", err) } got, err := repo.Get(ctx, "otto") if err != nil { t.Fatalf("Get failed: %v", err) } if got.Status != models.AgentStatusActive { t.Errorf("status after merge = %q, want %q", got.Status, models.AgentStatusActive) } } func TestInitialSync_BroadcastsFleet(t *testing.T) { repo := &mockAgentRepo{agents: make(map[string]models.AgentCardData)} broker := handler.NewBroker() capture := newBroadcastCapture(broker) defer capture.close() // Create some agents in the repo repo.agents["otto"] = models.AgentCardData{ID: "otto", DisplayName: "Otto", Status: models.AgentStatusActive} repo.agents["dex"] = models.AgentCardData{ID: "dex", DisplayName: "Dex", Status: models.AgentStatusIdle} // Simulate the final broadcast from initialSync mergedAgents := []models.AgentCardData{ repo.agents["otto"], repo.agents["dex"], } broker.Broadcast("fleet.update", mergedAgents) events := capture.captured() if len(events) == 0 { t.Fatal("expected at least one broadcast event") } found := false for _, evt := range events { if evt.EventType == "fleet.update" { found = true // Verify data is the merged agents list agents, ok := evt.Data.([]models.AgentCardData) if !ok { t.Fatalf("fleet.update data type = %T, want []models.AgentCardData", evt.Data) } if len(agents) != 2 { t.Errorf("fleet.update agents count = %d, want 2", len(agents)) } break } } if !found { t.Error("expected fleet.update broadcast event") } }