gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/rpc/rpcclient/notification_test.go (about) 1 // Copyright 2018 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The aquachain library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the aquachain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rpc 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "sync" 24 "testing" 25 "time" 26 27 "gitlab.com/aquachain/aquachain/rpc" 28 ) 29 30 type NotificationTestService struct { 31 mu sync.Mutex 32 unsubscribed bool 33 34 gotHangSubscriptionReq chan struct{} 35 unblockHangSubscription chan struct{} 36 } 37 38 func (s *NotificationTestService) Echo(i int) int { 39 return i 40 } 41 42 func (s *NotificationTestService) wasUnsubCallbackCalled() bool { 43 s.mu.Lock() 44 defer s.mu.Unlock() 45 return s.unsubscribed 46 } 47 48 func (s *NotificationTestService) Unsubscribe(subid string) { 49 s.mu.Lock() 50 s.unsubscribed = true 51 s.mu.Unlock() 52 } 53 54 func (s *NotificationTestService) SomeSubscription(ctx context.Context, n, val int) (*rpc.Subscription, error) { 55 notifier, supported := rpc.NotifierFromContext(ctx) 56 if !supported { 57 return nil, ErrNotificationsUnsupported 58 } 59 60 // by explicitly creating an subscription we make sure that the subscription id is send back to the client 61 // before the first subscription.Notify is called. Otherwise the events might be send before the response 62 // for the aqua_subscribe method. 63 subscription := notifier.CreateSubscription() 64 65 go func() { 66 // test expects n events, if we begin sending event immediately some events 67 // will probably be dropped since the subscription ID might not be send to 68 // the client. 69 time.Sleep(5 * time.Second) 70 for i := 0; i < n; i++ { 71 if err := notifier.Notify(subscription.ID, val+i); err != nil { 72 return 73 } 74 } 75 76 select { 77 case <-notifier.Closed(): 78 s.mu.Lock() 79 s.unsubscribed = true 80 s.mu.Unlock() 81 case <-subscription.Err(): 82 s.mu.Lock() 83 s.unsubscribed = true 84 s.mu.Unlock() 85 } 86 }() 87 88 return subscription, nil 89 } 90 91 // HangSubscription blocks on s.unblockHangSubscription before 92 // sending anything. 93 func (s *NotificationTestService) HangSubscription(ctx context.Context, val int) (*rpc.Subscription, error) { 94 notifier, supported := rpc.NotifierFromContext(ctx) 95 if !supported { 96 return nil, ErrNotificationsUnsupported 97 } 98 99 s.gotHangSubscriptionReq <- struct{}{} 100 <-s.unblockHangSubscription 101 subscription := notifier.CreateSubscription() 102 103 go func() { 104 notifier.Notify(subscription.ID, val) 105 }() 106 return subscription, nil 107 } 108 109 func waitForMessages(t *testing.T, in *json.Decoder, successes chan<- jsonSuccessResponse, 110 failures chan<- jsonErrResponse, notifications chan<- jsonNotification, errors chan<- error) { 111 112 // read and parse server messages 113 for { 114 var rmsg json.RawMessage 115 if err := in.Decode(&rmsg); err != nil { 116 return 117 } 118 119 var responses []map[string]interface{} 120 if rmsg[0] == '[' { 121 if err := json.Unmarshal(rmsg, &responses); err != nil { 122 errors <- fmt.Errorf("Received invalid message: %s", rmsg) 123 return 124 } 125 } else { 126 var msg map[string]interface{} 127 if err := json.Unmarshal(rmsg, &msg); err != nil { 128 errors <- fmt.Errorf("Received invalid message: %s", rmsg) 129 return 130 } 131 responses = append(responses, msg) 132 } 133 134 for _, msg := range responses { 135 // determine what kind of msg was received and broadcast 136 // it to over the corresponding channel 137 if _, found := msg["result"]; found { 138 successes <- jsonSuccessResponse{ 139 Version: msg["jsonrpc"].(string), 140 Id: msg["id"], 141 Result: msg["result"], 142 } 143 continue 144 } 145 if _, found := msg["error"]; found { 146 params := msg["params"].(map[string]interface{}) 147 failures <- jsonErrResponse{ 148 Version: msg["jsonrpc"].(string), 149 Id: msg["id"], 150 Error: rpc.JsonError{int(params["subscription"].(float64)), params["message"].(string), params["data"]}, 151 } 152 continue 153 } 154 if _, found := msg["params"]; found { 155 params := msg["params"].(map[string]interface{}) 156 notifications <- jsonNotification{ 157 Version: msg["jsonrpc"].(string), 158 Method: msg["method"].(string), 159 Params: jsonSubscription{params["subscription"].(string), params["result"]}, 160 } 161 continue 162 } 163 errors <- fmt.Errorf("Received invalid message: %s", msg) 164 } 165 } 166 }