diff --git a/pubsublite/admin.go b/pubsublite/admin.go index 0b7fd836445..4ba2ff52021 100644 --- a/pubsublite/admin.go +++ b/pubsublite/admin.go @@ -93,9 +93,10 @@ func (ac *AdminClient) TopicPartitions(ctx context.Context, topic TopicPath) (in } // TopicSubscriptions retrieves the list of subscription paths for a topic. -func (ac *AdminClient) TopicSubscriptions(ctx context.Context, topic TopicPath) (*SubscriptionPathIterator, error) { - subsPathIt := ac.admin.ListTopicSubscriptions(ctx, &pb.ListTopicSubscriptionsRequest{Name: topic.String()}) - return &SubscriptionPathIterator{it: subsPathIt}, nil +func (ac *AdminClient) TopicSubscriptions(ctx context.Context, topic TopicPath) *SubscriptionPathIterator { + return &SubscriptionPathIterator{ + it: ac.admin.ListTopicSubscriptions(ctx, &pb.ListTopicSubscriptionsRequest{Name: topic.String()}), + } } // Topics retrieves the list of topic configs for a given project and zone. diff --git a/pubsublite/admin_test.go b/pubsublite/admin_test.go new file mode 100644 index 00000000000..19c8c41e6f9 --- /dev/null +++ b/pubsublite/admin_test.go @@ -0,0 +1,373 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and + +package pubsublite + +import ( + "context" + "testing" + "time" + + "cloud.google.com/go/internal/testutil" + "cloud.google.com/go/pubsublite/internal/test" + "google.golang.org/api/iterator" + + emptypb "github.com/golang/protobuf/ptypes/empty" + pb "google.golang.org/genproto/googleapis/cloud/pubsublite/v1" +) + +func newTestAdminClient(t *testing.T) *AdminClient { + admin, err := NewAdminClient(context.Background(), "us-central1", testClientOpts...) + if err != nil { + t.Fatal(err) + } + return admin +} + +func TestAdminTopicCRUD(t *testing.T) { + ctx := context.Background() + + // Inputs + topicPath := TopicPath{Project: "my-proj", Zone: "us-central1-a", TopicID: "my-topic"} + topicConfig := TopicConfig{ + Name: topicPath, + PartitionCount: 2, + PublishCapacityMiBPerSec: 4, + SubscribeCapacityMiBPerSec: 4, + PerPartitionBytes: 30 * gibi, + RetentionDuration: time.Duration(24 * time.Hour), + } + updateConfig := TopicConfigToUpdate{ + Name: topicPath, + PublishCapacityMiBPerSec: 6, + SubscribeCapacityMiBPerSec: 8, + PerPartitionBytes: 40 * gibi, + RetentionDuration: InfiniteRetention, + } + + // Expected requests and fake responses + wantCreateReq := &pb.CreateTopicRequest{ + Parent: "projects/my-proj/locations/us-central1-a", + TopicId: "my-topic", + Topic: topicConfig.toProto(), + } + wantUpdateReq := updateConfig.toUpdateRequest() + wantGetReq := &pb.GetTopicRequest{ + Name: "projects/my-proj/locations/us-central1-a/topics/my-topic", + } + wantPartitionsReq := &pb.GetTopicPartitionsRequest{ + Name: "projects/my-proj/locations/us-central1-a/topics/my-topic", + } + wantDeleteReq := &pb.DeleteTopicRequest{ + Name: "projects/my-proj/locations/us-central1-a/topics/my-topic", + } + + verifiers := test.NewVerifiers(t) + verifiers.GlobalVerifier.Push(wantCreateReq, topicConfig.toProto(), nil) + verifiers.GlobalVerifier.Push(wantUpdateReq, topicConfig.toProto(), nil) + verifiers.GlobalVerifier.Push(wantGetReq, topicConfig.toProto(), nil) + verifiers.GlobalVerifier.Push(wantPartitionsReq, &pb.TopicPartitions{PartitionCount: 3}, nil) + verifiers.GlobalVerifier.Push(wantDeleteReq, &emptypb.Empty{}, nil) + mockServer.OnTestStart(verifiers) + defer mockServer.OnTestEnd() + + admin := newTestAdminClient(t) + + if gotConfig, err := admin.CreateTopic(ctx, topicConfig); err != nil { + t.Errorf("CreateTopic() got err: %v", err) + } else if !testutil.Equal(gotConfig, &topicConfig) { + t.Errorf("CreateTopic() got: %v\nwant: %v", gotConfig, topicConfig) + } + + if gotConfig, err := admin.UpdateTopic(ctx, updateConfig); err != nil { + t.Errorf("UpdateTopic() got err: %v", err) + } else if !testutil.Equal(gotConfig, &topicConfig) { + t.Errorf("UpdateTopic() got: %v\nwant: %v", gotConfig, topicConfig) + } + + if gotConfig, err := admin.Topic(ctx, topicPath); err != nil { + t.Errorf("Topic() got err: %v", err) + } else if !testutil.Equal(gotConfig, &topicConfig) { + t.Errorf("Topic() got: %v\nwant: %v", gotConfig, topicConfig) + } + + if gotPartitions, err := admin.TopicPartitions(ctx, topicPath); err != nil { + t.Errorf("TopicPartitions() got err: %v", err) + } else if wantPartitions := 3; gotPartitions != wantPartitions { + t.Errorf("TopicPartitions() got: %v\nwant: %v", gotPartitions, wantPartitions) + } + + if err := admin.DeleteTopic(ctx, topicPath); err != nil { + t.Errorf("DeleteTopic() got err: %v", err) + } +} + +func TestAdminListTopics(t *testing.T) { + ctx := context.Background() + + // Inputs + locationPath := LocationPath{Project: "my-proj", Zone: "us-central1-a"} + topicConfig1 := TopicConfig{ + Name: TopicPath{Project: "my-proj", Zone: "us-central1-a", TopicID: "topic1"}, + PartitionCount: 2, + PublishCapacityMiBPerSec: 4, + SubscribeCapacityMiBPerSec: 4, + PerPartitionBytes: 30 * gibi, + RetentionDuration: 24 * time.Hour, + } + topicConfig2 := TopicConfig{ + Name: TopicPath{Project: "my-proj", Zone: "us-central1-a", TopicID: "topic2"}, + PartitionCount: 4, + PublishCapacityMiBPerSec: 6, + SubscribeCapacityMiBPerSec: 8, + PerPartitionBytes: 50 * gibi, + RetentionDuration: InfiniteRetention, + } + topicConfig3 := TopicConfig{ + Name: TopicPath{Project: "my-proj", Zone: "us-central1-a", TopicID: "topic3"}, + PartitionCount: 3, + PublishCapacityMiBPerSec: 8, + SubscribeCapacityMiBPerSec: 12, + PerPartitionBytes: 60 * gibi, + RetentionDuration: 12 * time.Hour, + } + + // Expected requests and fake responses + wantListReq1 := &pb.ListTopicsRequest{ + Parent: "projects/my-proj/locations/us-central1-a", + } + listResp1 := &pb.ListTopicsResponse{ + Topics: []*pb.Topic{topicConfig1.toProto(), topicConfig2.toProto()}, + NextPageToken: "next_token", + } + wantListReq2 := &pb.ListTopicsRequest{ + Parent: "projects/my-proj/locations/us-central1-a", + PageToken: "next_token", + } + listResp2 := &pb.ListTopicsResponse{ + Topics: []*pb.Topic{topicConfig3.toProto()}, + } + + verifiers := test.NewVerifiers(t) + verifiers.GlobalVerifier.Push(wantListReq1, listResp1, nil) + verifiers.GlobalVerifier.Push(wantListReq2, listResp2, nil) + mockServer.OnTestStart(verifiers) + defer mockServer.OnTestEnd() + + admin := newTestAdminClient(t) + + var gotTopicConfigs []*TopicConfig + topicIt := admin.Topics(ctx, locationPath) + for { + topic, err := topicIt.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Errorf("TopicIterator.Next() got err: %v", err) + } else { + gotTopicConfigs = append(gotTopicConfigs, topic) + } + } + + wantTopicConfigs := []*TopicConfig{&topicConfig1, &topicConfig2, &topicConfig3} + if diff := testutil.Diff(gotTopicConfigs, wantTopicConfigs); diff != "" { + t.Errorf("Topics() got: -, want: +\n%s", diff) + } +} + +func TestAdminListTopicSubscriptions(t *testing.T) { + ctx := context.Background() + + // Expected requests and fake responses + wantListReq1 := &pb.ListTopicSubscriptionsRequest{ + Name: "projects/my-proj/locations/us-central1-a/topics/my-topic", + } + listResp1 := &pb.ListTopicSubscriptionsResponse{ + Subscriptions: []string{ + "projects/my-proj/locations/us-central1-a/subscriptions/subscription1", + "projects/my-proj/locations/us-central1-a/subscriptions/subscription2", + }, + NextPageToken: "next_token", + } + wantListReq2 := &pb.ListTopicSubscriptionsRequest{ + Name: "projects/my-proj/locations/us-central1-a/topics/my-topic", + PageToken: "next_token", + } + listResp2 := &pb.ListTopicSubscriptionsResponse{ + Subscriptions: []string{ + "projects/my-proj/locations/us-central1-a/subscriptions/subscription3", + }, + } + + verifiers := test.NewVerifiers(t) + verifiers.GlobalVerifier.Push(wantListReq1, listResp1, nil) + verifiers.GlobalVerifier.Push(wantListReq2, listResp2, nil) + mockServer.OnTestStart(verifiers) + defer mockServer.OnTestEnd() + + admin := newTestAdminClient(t) + + // Inputs + topicPath := TopicPath{Project: "my-proj", Zone: "us-central1-a", TopicID: "my-topic"} + subscription1 := SubscriptionPath{Project: "my-proj", Zone: "us-central1-a", SubscriptionID: "subscription1"} + subscription2 := SubscriptionPath{Project: "my-proj", Zone: "us-central1-a", SubscriptionID: "subscription2"} + subscription3 := SubscriptionPath{Project: "my-proj", Zone: "us-central1-a", SubscriptionID: "subscription3"} + + var gotSubscriptions []SubscriptionPath + subsPathIt := admin.TopicSubscriptions(ctx, topicPath) + for { + subsPath, err := subsPathIt.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Errorf("SubscriptionPathIterator.Next() got err: %v", err) + } else { + gotSubscriptions = append(gotSubscriptions, subsPath) + } + } + + wantSubscriptions := []SubscriptionPath{subscription1, subscription2, subscription3} + if !testutil.Equal(gotSubscriptions, wantSubscriptions) { + t.Errorf("TopicSubscriptions() got: %v\nwant: %v", gotSubscriptions, wantSubscriptions) + } +} + +func TestAdminSubscriptionCRUD(t *testing.T) { + ctx := context.Background() + + // Inputs + topicPath := TopicPath{Project: "my-proj", Zone: "us-central1-a", TopicID: "my-subscription"} + subscriptionPath := SubscriptionPath{Project: "my-proj", Zone: "us-central1-a", SubscriptionID: "my-subscription"} + subscriptionConfig := SubscriptionConfig{ + Name: subscriptionPath, + Topic: topicPath, + DeliveryRequirement: DeliverImmediately, + } + updateConfig := SubscriptionConfigToUpdate{ + Name: subscriptionPath, + DeliveryRequirement: DeliverAfterStored, + } + + // Expected requests and fake responses + wantCreateReq := &pb.CreateSubscriptionRequest{ + Parent: "projects/my-proj/locations/us-central1-a", + SubscriptionId: "my-subscription", + Subscription: subscriptionConfig.toProto(), + } + wantUpdateReq := updateConfig.toUpdateRequest() + wantGetReq := &pb.GetSubscriptionRequest{ + Name: "projects/my-proj/locations/us-central1-a/subscriptions/my-subscription", + } + wantDeleteReq := &pb.DeleteSubscriptionRequest{ + Name: "projects/my-proj/locations/us-central1-a/subscriptions/my-subscription", + } + + verifiers := test.NewVerifiers(t) + verifiers.GlobalVerifier.Push(wantCreateReq, subscriptionConfig.toProto(), nil) + verifiers.GlobalVerifier.Push(wantUpdateReq, subscriptionConfig.toProto(), nil) + verifiers.GlobalVerifier.Push(wantGetReq, subscriptionConfig.toProto(), nil) + verifiers.GlobalVerifier.Push(wantDeleteReq, &emptypb.Empty{}, nil) + mockServer.OnTestStart(verifiers) + defer mockServer.OnTestEnd() + + admin := newTestAdminClient(t) + + if gotConfig, err := admin.CreateSubscription(ctx, subscriptionConfig); err != nil { + t.Errorf("CreateSubscription() got err: %v", err) + } else if !testutil.Equal(gotConfig, &subscriptionConfig) { + t.Errorf("CreateSubscription() got: %v\nwant: %v", gotConfig, subscriptionConfig) + } + + if gotConfig, err := admin.UpdateSubscription(ctx, updateConfig); err != nil { + t.Errorf("UpdateSubscription() got err: %v", err) + } else if !testutil.Equal(gotConfig, &subscriptionConfig) { + t.Errorf("UpdateSubscription() got: %v\nwant: %v", gotConfig, subscriptionConfig) + } + + if gotConfig, err := admin.Subscription(ctx, subscriptionPath); err != nil { + t.Errorf("Subscription() got err: %v", err) + } else if !testutil.Equal(gotConfig, &subscriptionConfig) { + t.Errorf("Subscription() got: %v\nwant: %v", gotConfig, subscriptionConfig) + } + + if err := admin.DeleteSubscription(ctx, subscriptionPath); err != nil { + t.Errorf("DeleteSubscription() got err: %v", err) + } +} + +func TestAdminListSubscriptions(t *testing.T) { + ctx := context.Background() + + // Inputs + locationPath := LocationPath{Project: "my-proj", Zone: "us-central1-a"} + subscriptionConfig1 := SubscriptionConfig{ + Name: SubscriptionPath{Project: "my-proj", Zone: "us-central1-a", SubscriptionID: "subscription1"}, + Topic: TopicPath{Project: "my-proj", Zone: "us-central1-a", TopicID: "topic1"}, + DeliveryRequirement: DeliverImmediately, + } + subscriptionConfig2 := SubscriptionConfig{ + Name: SubscriptionPath{Project: "my-proj", Zone: "us-central1-a", SubscriptionID: "subscription2"}, + Topic: TopicPath{Project: "my-proj", Zone: "us-central1-a", TopicID: "topic2"}, + DeliveryRequirement: DeliverAfterStored, + } + subscriptionConfig3 := SubscriptionConfig{ + Name: SubscriptionPath{Project: "my-proj", Zone: "us-central1-a", SubscriptionID: "subscription3"}, + Topic: TopicPath{Project: "my-proj", Zone: "us-central1-a", TopicID: "topic3"}, + DeliveryRequirement: DeliverImmediately, + } + + // Expected requests and fake responses + wantListReq1 := &pb.ListSubscriptionsRequest{ + Parent: "projects/my-proj/locations/us-central1-a", + } + listResp1 := &pb.ListSubscriptionsResponse{ + Subscriptions: []*pb.Subscription{subscriptionConfig1.toProto(), subscriptionConfig2.toProto()}, + NextPageToken: "next_token", + } + wantListReq2 := &pb.ListSubscriptionsRequest{ + Parent: "projects/my-proj/locations/us-central1-a", + PageToken: "next_token", + } + listResp2 := &pb.ListSubscriptionsResponse{ + Subscriptions: []*pb.Subscription{subscriptionConfig3.toProto()}, + } + + verifiers := test.NewVerifiers(t) + verifiers.GlobalVerifier.Push(wantListReq1, listResp1, nil) + verifiers.GlobalVerifier.Push(wantListReq2, listResp2, nil) + mockServer.OnTestStart(verifiers) + defer mockServer.OnTestEnd() + + admin := newTestAdminClient(t) + + var gotSubscriptionConfigs []*SubscriptionConfig + subscriptionIt := admin.Subscriptions(ctx, locationPath) + for { + subscription, err := subscriptionIt.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Errorf("SubscriptionIterator.Next() got err: %v", err) + } else { + gotSubscriptionConfigs = append(gotSubscriptionConfigs, subscription) + } + } + + wantSubscriptionConfigs := []*SubscriptionConfig{&subscriptionConfig1, &subscriptionConfig2, &subscriptionConfig3} + if diff := testutil.Diff(gotSubscriptionConfigs, wantSubscriptionConfigs); diff != "" { + t.Errorf("Subscriptions() got: -, want: +\n%s", diff) + } +} diff --git a/pubsublite/integration_test.go b/pubsublite/integration_test.go index 7a5e1a3a50d..de8607c39dd 100644 --- a/pubsublite/integration_test.go +++ b/pubsublite/integration_test.go @@ -241,24 +241,21 @@ func TestResourceAdminOperations(t *testing.T) { t.Errorf("Subscriptions() found config: -, want: +\n%s", diff) } - if subsPathIt, err := admin.TopicSubscriptions(ctx, topicPath); err != nil { - t.Errorf("Failed to list topic subscriptions: %v", err) - } else { - foundSubsPath := false - for { - subsPath, err := subsPathIt.Next() - if err == iterator.Done { - break - } - if testutil.Equal(subsPath, subscriptionPath) { - foundSubsPath = true - break - } + subsPathIt := admin.TopicSubscriptions(ctx, topicPath) + foundSubsPath := false + for { + subsPath, err := subsPathIt.Next() + if err == iterator.Done { + break } - if !foundSubsPath { - t.Error("TopicSubscriptions() did not return subscription path") + if testutil.Equal(subsPath, subscriptionPath) { + foundSubsPath = true + break } } + if !foundSubsPath { + t.Error("TopicSubscriptions() did not return subscription path") + } subsUpdate := SubscriptionConfigToUpdate{ Name: subscriptionPath, diff --git a/pubsublite/internal/test/mock.go b/pubsublite/internal/test/mock.go index bf74683de36..1ddfa91e538 100644 --- a/pubsublite/internal/test/mock.go +++ b/pubsublite/internal/test/mock.go @@ -25,6 +25,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + emptypb "github.com/golang/protobuf/ptypes/empty" pb "google.golang.org/genproto/googleapis/cloud/pubsublite/v1" ) @@ -268,6 +269,54 @@ func (s *mockLiteServer) AssignPartitions(stream pb.PartitionAssignmentService_A // AdminService implementation. +func (s *mockLiteServer) doTopicResponse(ctx context.Context, req interface{}) (*pb.Topic, error) { + retResponse, retErr := s.popGlobalVerifiers(req) + if retErr != nil { + return nil, retErr + } + resp, ok := retResponse.(*pb.Topic) + if !ok { + return nil, status.Errorf(codes.FailedPrecondition, "mockserver: invalid response type %v", reflect.TypeOf(retResponse)) + } + return resp, nil +} + +func (s *mockLiteServer) doSubscriptionResponse(ctx context.Context, req interface{}) (*pb.Subscription, error) { + retResponse, retErr := s.popGlobalVerifiers(req) + if retErr != nil { + return nil, retErr + } + resp, ok := retResponse.(*pb.Subscription) + if !ok { + return nil, status.Errorf(codes.FailedPrecondition, "mockserver: invalid response type %v", reflect.TypeOf(retResponse)) + } + return resp, nil +} + +func (s *mockLiteServer) doEmptyResponse(ctx context.Context, req interface{}) (*emptypb.Empty, error) { + retResponse, retErr := s.popGlobalVerifiers(req) + if retErr != nil { + return nil, retErr + } + resp, ok := retResponse.(*emptypb.Empty) + if !ok { + return nil, status.Errorf(codes.FailedPrecondition, "mockserver: invalid response type %v", reflect.TypeOf(retResponse)) + } + return resp, nil +} + +func (s *mockLiteServer) CreateTopic(ctx context.Context, req *pb.CreateTopicRequest) (*pb.Topic, error) { + return s.doTopicResponse(ctx, req) +} + +func (s *mockLiteServer) UpdateTopic(ctx context.Context, req *pb.UpdateTopicRequest) (*pb.Topic, error) { + return s.doTopicResponse(ctx, req) +} + +func (s *mockLiteServer) GetTopic(ctx context.Context, req *pb.GetTopicRequest) (*pb.Topic, error) { + return s.doTopicResponse(ctx, req) +} + func (s *mockLiteServer) GetTopicPartitions(ctx context.Context, req *pb.GetTopicPartitionsRequest) (*pb.TopicPartitions, error) { retResponse, retErr := s.popGlobalVerifiers(req) if retErr != nil { @@ -279,3 +328,59 @@ func (s *mockLiteServer) GetTopicPartitions(ctx context.Context, req *pb.GetTopi } return resp, nil } + +func (s *mockLiteServer) DeleteTopic(ctx context.Context, req *pb.DeleteTopicRequest) (*emptypb.Empty, error) { + return s.doEmptyResponse(ctx, req) +} + +func (s *mockLiteServer) CreateSubscription(ctx context.Context, req *pb.CreateSubscriptionRequest) (*pb.Subscription, error) { + return s.doSubscriptionResponse(ctx, req) +} + +func (s *mockLiteServer) GetSubscription(ctx context.Context, req *pb.GetSubscriptionRequest) (*pb.Subscription, error) { + return s.doSubscriptionResponse(ctx, req) +} + +func (s *mockLiteServer) UpdateSubscription(ctx context.Context, req *pb.UpdateSubscriptionRequest) (*pb.Subscription, error) { + return s.doSubscriptionResponse(ctx, req) +} + +func (s *mockLiteServer) DeleteSubscription(ctx context.Context, req *pb.DeleteSubscriptionRequest) (*emptypb.Empty, error) { + return s.doEmptyResponse(ctx, req) +} + +func (s *mockLiteServer) ListTopics(ctx context.Context, req *pb.ListTopicsRequest) (*pb.ListTopicsResponse, error) { + retResponse, retErr := s.popGlobalVerifiers(req) + if retErr != nil { + return nil, retErr + } + resp, ok := retResponse.(*pb.ListTopicsResponse) + if !ok { + return nil, status.Errorf(codes.FailedPrecondition, "mockserver: invalid response type %v", reflect.TypeOf(retResponse)) + } + return resp, nil +} + +func (s *mockLiteServer) ListTopicSubscriptions(ctx context.Context, req *pb.ListTopicSubscriptionsRequest) (*pb.ListTopicSubscriptionsResponse, error) { + retResponse, retErr := s.popGlobalVerifiers(req) + if retErr != nil { + return nil, retErr + } + resp, ok := retResponse.(*pb.ListTopicSubscriptionsResponse) + if !ok { + return nil, status.Errorf(codes.FailedPrecondition, "mockserver: invalid response type %v", reflect.TypeOf(retResponse)) + } + return resp, nil +} + +func (s *mockLiteServer) ListSubscriptions(ctx context.Context, req *pb.ListSubscriptionsRequest) (*pb.ListSubscriptionsResponse, error) { + retResponse, retErr := s.popGlobalVerifiers(req) + if retErr != nil { + return nil, retErr + } + resp, ok := retResponse.(*pb.ListSubscriptionsResponse) + if !ok { + return nil, status.Errorf(codes.FailedPrecondition, "mockserver: invalid response type %v", reflect.TypeOf(retResponse)) + } + return resp, nil +} diff --git a/pubsublite/main_test.go b/pubsublite/main_test.go new file mode 100644 index 00000000000..e77724676e8 --- /dev/null +++ b/pubsublite/main_test.go @@ -0,0 +1,50 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and + +package pubsublite + +import ( + "flag" + "log" + "os" + "testing" + + "cloud.google.com/go/pubsublite/internal/test" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +var ( + // Initialized in TestMain. + mockServer test.MockServer + testClientOpts []option.ClientOption +) + +func TestMain(m *testing.M) { + flag.Parse() + + testServer, err := test.NewServer() + if err != nil { + log.Fatal(err) + } + mockServer = testServer.LiteServer + conn, err := grpc.Dial(testServer.Addr(), grpc.WithInsecure()) + if err != nil { + log.Fatal(err) + } + testClientOpts = []option.ClientOption{option.WithGRPCConn(conn)} + + exit := m.Run() + testServer.Close() + os.Exit(exit) +}