github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/services/remotecluster/send_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package remotecluster 5 6 import ( 7 "context" 8 "encoding/json" 9 "fmt" 10 "net/http" 11 "net/http/httptest" 12 "sync" 13 "sync/atomic" 14 "testing" 15 "time" 16 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 "github.com/wiggin77/merror" 20 21 "github.com/masterhung0112/hk_server/v5/model" 22 ) 23 24 const ( 25 TestTopics = " share incident " 26 TestTopic = "share" 27 NumRemotes = 50 28 NoteContent = "Woot!!" 29 ) 30 31 type testPayload struct { 32 Note string `json:"note"` 33 } 34 35 func TestBroadcastMsg(t *testing.T) { 36 msgId := model.NewId() 37 disablePing = true 38 39 t.Run("No error", func(t *testing.T) { 40 var countCallbacks int32 41 var countWebReq int32 42 merr := merror.New() 43 44 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 45 defer func() { 46 w.WriteHeader(200) 47 var resp Response 48 b, errMarshall := json.Marshal(&resp) 49 if errMarshall != nil { 50 merr.Append(errMarshall) 51 return 52 } 53 w.Write(b) 54 }() 55 56 atomic.AddInt32(&countWebReq, 1) 57 58 frame, appErr := model.RemoteClusterFrameFromJSON(r.Body) 59 if appErr != nil { 60 merr.Append(appErr) 61 return 62 } 63 if len(frame.Msg.Payload) == 0 { 64 merr.Append(fmt.Errorf("webrequest missing Msg.Payload")) 65 } 66 if msgId != frame.Msg.Id { 67 merr.Append(fmt.Errorf("webrequest msgId expected %s, got %s", msgId, frame.Msg.Id)) 68 return 69 } 70 71 note := testPayload{} 72 err := json.Unmarshal(frame.Msg.Payload, ¬e) 73 if err != nil { 74 merr.Append(err) 75 return 76 } 77 if note.Note != NoteContent { 78 merr.Append(fmt.Errorf("webrequest payload expected %s, got %s", NoteContent, note.Note)) 79 return 80 } 81 })) 82 defer ts.Close() 83 84 mockServer := newMockServer(t, makeRemoteClusters(NumRemotes, ts.URL)) 85 defer mockServer.Shutdown() 86 87 service, err := NewRemoteClusterService(mockServer) 88 require.NoError(t, err) 89 90 err = service.Start() 91 require.NoError(t, err) 92 defer service.Shutdown() 93 94 wg := &sync.WaitGroup{} 95 wg.Add(NumRemotes) 96 97 msg := makeRemoteClusterMsg(msgId, NoteContent) 98 99 ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) 100 defer cancel() 101 102 err = service.BroadcastMsg(ctx, msg, func(msg model.RemoteClusterMsg, remote *model.RemoteCluster, resp *Response, err error) { 103 defer wg.Done() 104 atomic.AddInt32(&countCallbacks, 1) 105 106 if err != nil { 107 merr.Append(err) 108 } 109 if msgId != msg.Id { 110 merr.Append(fmt.Errorf("result callback msgId expected %s, got %s", msgId, msg.Id)) 111 } 112 113 var note testPayload 114 err2 := json.Unmarshal(msg.Payload, ¬e) 115 if err2 != nil { 116 merr.Append(fmt.Errorf("unmarshal payload error: %w", err2)) 117 return 118 } 119 if note.Note != NoteContent { 120 merr.Append(fmt.Errorf("compare payload failed: expected '%s', got '%s'", NoteContent, note)) 121 } 122 }) 123 assert.NoError(t, err) 124 125 wg.Wait() 126 127 assert.NoError(t, merr.ErrorOrNil()) 128 129 assert.Equal(t, int32(NumRemotes), atomic.LoadInt32(&countCallbacks)) 130 assert.Equal(t, int32(NumRemotes), atomic.LoadInt32(&countWebReq)) 131 t.Log(fmt.Sprintf("%d callbacks counted; %d web requests counted; %d expected", 132 atomic.LoadInt32(&countCallbacks), atomic.LoadInt32(&countWebReq), NumRemotes)) 133 }) 134 135 t.Run("HTTP error", func(t *testing.T) { 136 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 137 w.WriteHeader(500) 138 })) 139 defer ts.Close() 140 141 mockServer := newMockServer(t, makeRemoteClusters(NumRemotes, ts.URL)) 142 defer mockServer.Shutdown() 143 144 service, err := NewRemoteClusterService(mockServer) 145 require.NoError(t, err) 146 147 err = service.Start() 148 require.NoError(t, err) 149 defer service.Shutdown() 150 151 msg := makeRemoteClusterMsg(msgId, NoteContent) 152 var countCallbacks int32 153 var countErrors int32 154 wg := &sync.WaitGroup{} 155 wg.Add(NumRemotes) 156 157 err = service.BroadcastMsg(context.Background(), msg, func(msg model.RemoteClusterMsg, remote *model.RemoteCluster, resp *Response, err error) { 158 defer wg.Done() 159 atomic.AddInt32(&countCallbacks, 1) 160 if err != nil { 161 atomic.AddInt32(&countErrors, 1) 162 } 163 }) 164 assert.NoError(t, err) 165 166 wg.Wait() 167 168 assert.Equal(t, int32(NumRemotes), atomic.LoadInt32(&countCallbacks)) 169 assert.Equal(t, int32(NumRemotes), atomic.LoadInt32(&countErrors)) 170 }) 171 } 172 173 func makeRemoteClusters(num int, siteURL string) []*model.RemoteCluster { 174 var remotes []*model.RemoteCluster 175 for i := 0; i < num; i++ { 176 rc := makeRemoteCluster(fmt.Sprintf("test cluster %d", i+1), siteURL, TestTopics) 177 remotes = append(remotes, rc) 178 } 179 return remotes 180 } 181 182 func makeRemoteCluster(name string, siteURL string, topics string) *model.RemoteCluster { 183 return &model.RemoteCluster{ 184 RemoteId: model.NewId(), 185 Name: name, 186 SiteURL: siteURL, 187 Token: model.NewId(), 188 Topics: topics, 189 CreateAt: model.GetMillis(), 190 LastPingAt: model.GetMillis(), 191 CreatorId: model.NewId(), 192 } 193 } 194 195 func makeRemoteClusterMsg(id string, note string) model.RemoteClusterMsg { 196 payload := testPayload{Note: note} 197 raw, _ := json.Marshal(payload) 198 199 return model.RemoteClusterMsg{ 200 Id: id, 201 Topic: TestTopic, 202 CreateAt: model.GetMillis(), 203 Payload: raw} 204 }