From 7bbb7073babd275fb1ca4f3642334ddcd25ca1fa Mon Sep 17 00:00:00 2001 From: "nikolay.bystritskiy" Date: Fri, 7 May 2021 22:27:37 +0200 Subject: [PATCH] unittest, typos --- app/discovery/discovery.go | 3 +- app/discovery/discovery_test.go | 54 +++++++++++++++++++++ app/discovery/health.go | 2 + app/discovery/health_test.go | 85 +++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 1 deletion(-) diff --git a/app/discovery/discovery.go b/app/discovery/discovery.go index 2506363..082953e 100644 --- a/app/discovery/discovery.go +++ b/app/discovery/discovery.go @@ -151,7 +151,7 @@ func (s *Service) Match(srv, src string) (string, MatchType, bool) { // ScheduleHealthCheck starts background loop with health-check func (s *Service) ScheduleHealthCheck(ctx context.Context, interval time.Duration) { - log.Printf("health-check scheduled every %s seconds", interval) + log.Printf("health-check scheduled every %s", interval) go func() { hloop: @@ -163,6 +163,7 @@ func (s *Service) ScheduleHealthCheck(ctx context.Context, interval time.Duratio cres := CheckHealth(s.Mappers()) s.lock.RUnlock() + // alive services would be picked up first sort.SliceStable(cres.mappers, func(i, j int) bool { return cres.mappers[j].dead }) diff --git a/app/discovery/discovery_test.go b/app/discovery/discovery_test.go index b90c23c..41005fc 100644 --- a/app/discovery/discovery_test.go +++ b/app/discovery/discovery_test.go @@ -3,6 +3,10 @@ package discovery import ( "context" "errors" + "fmt" + "math/rand" + "net/http" + "net/http/httptest" "regexp" "strconv" "testing" @@ -223,3 +227,53 @@ func TestService_extendRule(t *testing.T) { } } + +func TestService_ScheduleHealthCheck(t *testing.T) { + randomPort := rand.Intn(10000) + 40000 + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK")) + })) + defer ts.Close() + + ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK")) + })) + defer ts2.Close() + + wantMappers := []URLMapper{ + {SrcMatch: *regexp.MustCompile("/api/svc3/xyz"), Dst: "http://127.0.0.3:8080/blah3/xyz", ProviderID: PIDocker, PingURL: ts.URL}, + {SrcMatch: *regexp.MustCompile("/api/svc3/xyz"), Dst: "http://127.0.0.3:8080/blah3/xyz", ProviderID: PIDocker, PingURL: fmt.Sprintf("127.0.0.1:%d", randomPort)}, + {SrcMatch: *regexp.MustCompile("/api/svc3/xyz"), Dst: "http://127.0.0.3:8080/blah3/xyz", ProviderID: PIDocker, PingURL: ts2.URL}, + } + + p := &ProviderMock{ + EventsFunc: func(ctx context.Context) <-chan ProviderID { + res := make(chan ProviderID, 1) + res <- PIFile + return res + }, + ListFunc: func() ([]URLMapper, error) { + return wantMappers, nil + }, + } + + svc := NewService([]Provider{p}, time.Millisecond*10) + ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) + defer cancel() + + err := svc.Run(ctx) + require.Error(t, err) + assert.Equal(t, context.DeadlineExceeded, err) + mappers := svc.Mappers() + assert.Equal(t, 3, len(mappers)) + assert.Equal(t, wantMappers, mappers) + + svc.ScheduleHealthCheck(context.Background(), time.Microsecond*2) + time.Sleep(time.Millisecond * 10) + + mappers = svc.Mappers() + assert.Equal(t, false, mappers[0].dead) + assert.Equal(t, false, mappers[1].dead) + assert.Equal(t, true, mappers[2].dead) +} diff --git a/app/discovery/health.go b/app/discovery/health.go index f561a58..d2d5e52 100644 --- a/app/discovery/health.go +++ b/app/discovery/health.go @@ -9,6 +9,7 @@ import ( "time" ) +// CheckResult is result of health-check type CheckResult struct { Ok bool Valid int @@ -17,6 +18,7 @@ type CheckResult struct { mappers []URLMapper } +// CheckHealth starts health-check for service's mappers func CheckHealth(mappers []URLMapper) CheckResult { const concurrent = 8 sema := make(chan struct{}, concurrent) // limit health check to 8 concurrent calls diff --git a/app/discovery/health_test.go b/app/discovery/health_test.go index 5844159..1726b9c 100644 --- a/app/discovery/health_test.go +++ b/app/discovery/health_test.go @@ -1 +1,86 @@ package discovery + +import ( + "fmt" + "math/rand" + "net/http" + "net/http/httptest" + "testing" +) + +func Test_ping(t *testing.T) { + port := rand.Intn(10000) + 40000 + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK")) + })) + defer ts.Close() + + ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(500) + })) + defer ts2.Close() + + type args struct { + m URLMapper + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + {name: "test server, expected OK", args: args{m: URLMapper{PingURL: ts.URL}}, want: "", wantErr: false}, + {name: "random port, expected error", args: args{m: URLMapper{PingURL: fmt.Sprintf("127.0.0.1:%d", port)}}, want: "", wantErr: true}, + {name: "error code != 200", args: args{m: URLMapper{PingURL: ts2.URL}}, want: "", wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := ping(tt.args.m) + if (err != nil) != tt.wantErr { + t.Errorf("ping() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} + +func TestCheckHealth(t *testing.T) { + port := rand.Intn(10000) + 40000 + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK")) + })) + defer ts.Close() + + ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK")) + })) + defer ts2.Close() + + type args struct { + mappers []URLMapper + } + tests := []struct { + name string + args args + want CheckResult + }{ + {name: "case 1", args: args{mappers: []URLMapper{{PingURL: ts.URL}, {PingURL: ts2.URL}, {PingURL: fmt.Sprintf("127.0.0.1:%d", port)}}}, + want: CheckResult{Ok: false, Total: 3, Valid: 2, mappers: []URLMapper{{PingURL: ts.URL, dead: false}, {PingURL: ts2.URL, dead: false}, + {PingURL: fmt.Sprintf("127.0.0.1:%d", port), dead: true}}}}, + {name: "case 2", args: args{mappers: []URLMapper{{PingURL: ts.URL}, {PingURL: ts2.URL}}}, + want: CheckResult{Ok: true, Total: 2, Valid: 2, mappers: []URLMapper{{PingURL: ts.URL, dead: false}, {PingURL: ts2.URL, dead: false}}}}, + {name: "case 3", args: args{mappers: []URLMapper{{PingURL: ts.URL, MatchType: MTStatic}, {PingURL: ts2.URL}}}, + want: CheckResult{Ok: true, Total: 1, Valid: 1, mappers: []URLMapper{{PingURL: ts.URL, dead: false}, {PingURL: ts2.URL, dead: false}}}}, + {name: "case 4", args: args{mappers: []URLMapper{{}, {PingURL: ts2.URL}}}, + want: CheckResult{Ok: true, Total: 2, Valid: 1, mappers: []URLMapper{{PingURL: ts.URL, dead: false}, {PingURL: ts2.URL, dead: true}}}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := CheckHealth(tt.args.mappers) + got.Errs = got.Errs[:0] + if got.Ok != tt.want.Ok || got.Total != tt.want.Total || got.Valid != tt.want.Valid { + t.Errorf("CheckHealth() = %v, want %v", got, tt.want) + } + }) + } +}