google.golang.org/grpc@v1.74.2/health/server_internal_test.go (about) 1 /* 2 * 3 * Copyright 2018 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package health 20 21 import ( 22 "context" 23 "errors" 24 "fmt" 25 "sync" 26 "testing" 27 "time" 28 29 "github.com/google/go-cmp/cmp" 30 "google.golang.org/grpc/codes" 31 "google.golang.org/grpc/status" 32 "google.golang.org/protobuf/testing/protocmp" 33 34 healthpb "google.golang.org/grpc/health/grpc_health_v1" 35 "google.golang.org/grpc/internal/grpctest" 36 ) 37 38 type s struct { 39 grpctest.Tester 40 } 41 42 func Test(t *testing.T) { 43 grpctest.RunSubTests(t, s{}) 44 } 45 46 func (s) TestShutdown(t *testing.T) { 47 const testService = "tteesstt" 48 s := NewServer() 49 s.SetServingStatus(testService, healthpb.HealthCheckResponse_SERVING) 50 51 status := s.statusMap[testService] 52 if status != healthpb.HealthCheckResponse_SERVING { 53 t.Fatalf("status for %s is %v, want %v", testService, status, healthpb.HealthCheckResponse_SERVING) 54 } 55 56 var wg sync.WaitGroup 57 wg.Add(2) 58 // Run SetServingStatus and Shutdown in parallel. 59 go func() { 60 for i := 0; i < 1000; i++ { 61 s.SetServingStatus(testService, healthpb.HealthCheckResponse_SERVING) 62 time.Sleep(time.Microsecond) 63 } 64 wg.Done() 65 }() 66 go func() { 67 time.Sleep(300 * time.Microsecond) 68 s.Shutdown() 69 wg.Done() 70 }() 71 wg.Wait() 72 73 s.mu.Lock() 74 status = s.statusMap[testService] 75 s.mu.Unlock() 76 if status != healthpb.HealthCheckResponse_NOT_SERVING { 77 t.Fatalf("status for %s is %v, want %v", testService, status, healthpb.HealthCheckResponse_NOT_SERVING) 78 } 79 80 s.Resume() 81 status = s.statusMap[testService] 82 if status != healthpb.HealthCheckResponse_SERVING { 83 t.Fatalf("status for %s is %v, want %v", testService, status, healthpb.HealthCheckResponse_SERVING) 84 } 85 86 s.SetServingStatus(testService, healthpb.HealthCheckResponse_NOT_SERVING) 87 status = s.statusMap[testService] 88 if status != healthpb.HealthCheckResponse_NOT_SERVING { 89 t.Fatalf("status for %s is %v, want %v", testService, status, healthpb.HealthCheckResponse_NOT_SERVING) 90 } 91 } 92 93 // TestList verifies that List() returns the health status of all the services if no. of services are within 94 // maxAllowedLimits. 95 func (s) TestList(t *testing.T) { 96 s := NewServer() 97 98 // Fill out status map with information 99 const length = 3 100 for i := 0; i < length; i++ { 101 s.SetServingStatus(fmt.Sprintf("%d", i), 102 healthpb.HealthCheckResponse_ServingStatus(i)) 103 } 104 105 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 106 defer cancel() 107 var in healthpb.HealthListRequest 108 got, err := s.List(ctx, &in) 109 110 if err != nil { 111 t.Fatalf("s.List(ctx, &in) returned err %v, want nil", err) 112 } 113 if len(got.GetStatuses()) != length+1 { 114 t.Fatalf("len(out.GetStatuses()) = %d, want %d", 115 len(got.GetStatuses()), length+1) 116 } 117 want := &healthpb.HealthListResponse{ 118 Statuses: map[string]*healthpb.HealthCheckResponse{ 119 "": {Status: healthpb.HealthCheckResponse_SERVING}, 120 "0": {Status: healthpb.HealthCheckResponse_UNKNOWN}, 121 "1": {Status: healthpb.HealthCheckResponse_SERVING}, 122 "2": {Status: healthpb.HealthCheckResponse_NOT_SERVING}, 123 }, 124 } 125 if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" { 126 t.Fatalf("Health response did not match expectation. Diff (-got, +want): %s", diff) 127 } 128 } 129 130 // TestListResourceExhausted verifies that List() 131 // returns a ResourceExhausted error if no. of services are more than 132 // maxAllowedServices. 133 func (s) TestListResourceExhausted(t *testing.T) { 134 s := NewServer() 135 136 // Fill out status map with service information, 137 // 101 (100 + 1 existing) elements will trigger an error. 138 for i := 1; i <= maxAllowedServices; i++ { 139 s.SetServingStatus(fmt.Sprintf("%d", i), 140 healthpb.HealthCheckResponse_SERVING) 141 } 142 143 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 144 defer cancel() 145 var in healthpb.HealthListRequest 146 _, err := s.List(ctx, &in) 147 148 want := status.Errorf(codes.ResourceExhausted, 149 "server health list exceeds maximum capacity: %d", maxAllowedServices) 150 if !errors.Is(err, want) { 151 t.Fatalf("s.List(ctx, &in) returned %v, want %v", err, want) 152 } 153 }