diff --git a/main/commands/all/api/balancer_info.go b/main/commands/all/api/balancer_info.go index cabc66d98380..97ab9f7ba772 100644 --- a/main/commands/all/api/balancer_info.go +++ b/main/commands/all/api/balancer_info.go @@ -47,18 +47,19 @@ func executeBalancerInfo(cmd *base.Command, args []string) { conn, ctx, close := dialAPIServer() defer close() client := routerService.NewRoutingServiceClient(conn) - r := &routerService.GetBalancerInfoRequest{Tag: unnamedArgs[0]} - resp, err := client.GetBalancerInfo(ctx, r) - if err != nil { - base.Fatalf("failed to get health information: %s", err) - } + for _, tag := range unnamedArgs { + r := &routerService.GetBalancerInfoRequest{Tag: tag} + resp, err := client.GetBalancerInfo(ctx, r) + if err != nil { + base.Fatalf("failed to get health information: %s", err) + } - if apiJSON { - showJSONResponse(resp) - return + if apiJSON { + showJSONResponse(resp) + } else { + showBalancerInfo(resp.Balancer) + } } - - showBalancerInfo(resp.Balancer) } func showBalancerInfo(b *routerService.BalancerMsg) { diff --git a/main/commands/all/api/balancer_info_test.go b/main/commands/all/api/balancer_info_test.go new file mode 100644 index 000000000000..8f4feb1836d5 --- /dev/null +++ b/main/commands/all/api/balancer_info_test.go @@ -0,0 +1,58 @@ +package api + +import ( + "context" + "net" + "slices" + "testing" + + "google.golang.org/grpc" + + routerService "github.com/xtls/xray-core/app/router/command" + "github.com/xtls/xray-core/main/commands/base" +) + +type balancerInfoRecorder struct { + routerService.UnimplementedRoutingServiceServer + tags chan string +} + +func (s *balancerInfoRecorder) GetBalancerInfo(_ context.Context, request *routerService.GetBalancerInfoRequest) (*routerService.GetBalancerInfoResponse, error) { + s.tags <- request.Tag + return &routerService.GetBalancerInfoResponse{Balancer: &routerService.BalancerMsg{}}, nil +} + +func TestBalancerInfoMultipleTags(t *testing.T) { + serverAddr, timeout, jsonOutput := apiServerAddrPtr, apiTimeout, apiJSON + t.Cleanup(func() { apiServerAddrPtr, apiTimeout, apiJSON = serverAddr, timeout, jsonOutput }) + for _, mode := range []string{"text", "json"} { + t.Run(mode, func(t *testing.T) { + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + recorder := &balancerInfoRecorder{tags: make(chan string, 2)} + server := grpc.NewServer() + routerService.RegisterRoutingServiceServer(server, recorder) + stopped := make(chan struct{}) + go func() { + defer close(stopped) + server.Serve(listener) + }() + t.Cleanup(func() { server.Stop(); <-stopped }) + + args := []string{"--server=" + listener.Addr().String()} + if mode == "json" { + args = append(args, "--json") + } + executeBalancerInfo(&base.Command{}, append(args, "a", "b")) + var got []string + for len(recorder.tags) > 0 { + got = append(got, <-recorder.tags) + } + if !slices.Equal(got, []string{"a", "b"}) { + t.Fatalf("queried tags = %v, want [a b]", got) + } + }) + } +}