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  }