github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/communication/nats/connection_mock.go (about) 1 /* 2 * Copyright (C) 2017 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU 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 * This program 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 General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package nats 19 20 import ( 21 "context" 22 "sync" 23 "time" 24 25 "github.com/nats-io/nats.go" 26 "github.com/pkg/errors" 27 ) 28 29 // NewConnectionMock constructs new NATS connection 30 // which delivers published messages to local subscribers 31 func NewConnectionMock() *ConnectionMock { 32 return &ConnectionMock{ 33 subscriptions: make(map[string][]nats.MsgHandler), 34 queue: make(chan *nats.Msg), 35 queueShutdown: make(chan bool), 36 } 37 } 38 39 // StartConnectionMock creates connection and starts it immediately 40 func StartConnectionMock() *ConnectionMock { 41 connection := NewConnectionMock() 42 connection.Open() 43 44 return connection 45 } 46 47 // ConnectionMock acts as a local connection implementation 48 type ConnectionMock struct { 49 subscriptions map[string][]nats.MsgHandler 50 queue chan *nats.Msg 51 queueShutdown chan bool 52 53 messageLast *nats.Msg 54 m sync.Mutex 55 requestLast *nats.Msg 56 errorMock error 57 } 58 59 // GetLastMessageSubject returns the last message subject 60 func (conn *ConnectionMock) GetLastMessageSubject() string { 61 if conn.messageLast != nil { 62 return conn.messageLast.Subject 63 } 64 return "" 65 } 66 67 // GetLastMessage returns the last message received 68 func (conn *ConnectionMock) GetLastMessage() []byte { 69 if conn.messageLast != nil { 70 return conn.messageLast.Data 71 } 72 return []byte{} 73 } 74 75 // GetLastRequest gets last request data 76 func (conn *ConnectionMock) GetLastRequest() []byte { 77 if conn.requestLast != nil { 78 return conn.requestLast.Data 79 } 80 return []byte{} 81 } 82 83 // MockResponse mocks the response 84 func (conn *ConnectionMock) MockResponse(subject string, payload []byte) { 85 conn.Subscribe(subject, func(message *nats.Msg) { 86 conn.Publish(message.Reply, payload) 87 }) 88 } 89 90 // MockError mocks the error 91 func (conn *ConnectionMock) MockError(message string) { 92 conn.errorMock = errors.New(message) 93 } 94 95 // MessageWait waits for a message to arrive 96 func (conn *ConnectionMock) MessageWait(waitChannel chan interface{}) (interface{}, error) { 97 select { 98 case message := <-waitChannel: 99 return message, nil 100 case <-time.After(10 * time.Millisecond): 101 return nil, errors.New("Message not received") 102 } 103 } 104 105 // Publish publishes a new message 106 func (conn *ConnectionMock) Publish(subject string, payload []byte) error { 107 if conn.errorMock != nil { 108 return conn.errorMock 109 } 110 111 conn.m.Lock() 112 defer conn.m.Unlock() 113 conn.messageLast = &nats.Msg{ 114 Subject: subject, 115 Data: payload, 116 } 117 conn.queue <- conn.messageLast 118 119 return nil 120 } 121 122 // Subscribe subscribes to a topic 123 func (conn *ConnectionMock) Subscribe(subject string, handler nats.MsgHandler) (*nats.Subscription, error) { 124 if conn.errorMock != nil { 125 return nil, conn.errorMock 126 } 127 128 conn.subscriptionAdd(subject, handler) 129 130 return &nats.Subscription{}, nil 131 } 132 133 // Request sends a new request 134 func (conn *ConnectionMock) Request(subject string, payload []byte, timeout time.Duration) (*nats.Msg, error) { 135 if conn.errorMock != nil { 136 return nil, conn.errorMock 137 } 138 139 subjectReply := subject + "-reply" 140 responseCh := make(chan *nats.Msg) 141 conn.Subscribe(subjectReply, func(response *nats.Msg) { 142 responseCh <- response 143 }) 144 145 conn.requestLast = &nats.Msg{ 146 Subject: subject, 147 Reply: subjectReply, 148 Data: payload, 149 } 150 conn.queue <- conn.requestLast 151 152 select { 153 case response := <-responseCh: 154 return response, nil 155 case <-time.After(timeout): 156 return nil, errors.Errorf("request '%s' timeout", subject) 157 } 158 } 159 160 // RequestWithContext Request sends a new request with context 161 func (conn *ConnectionMock) RequestWithContext(ctx context.Context, subject string, payload []byte) (*nats.Msg, error) { 162 if conn.errorMock != nil { 163 return nil, conn.errorMock 164 } 165 166 subjectReply := subject + "-reply" 167 responseCh := make(chan *nats.Msg) 168 conn.Subscribe(subjectReply, func(response *nats.Msg) { 169 responseCh <- response 170 }) 171 172 conn.requestLast = &nats.Msg{ 173 Subject: subject, 174 Reply: subjectReply, 175 Data: payload, 176 } 177 conn.queue <- conn.requestLast 178 179 select { 180 case response := <-responseCh: 181 return response, nil 182 case <-ctx.Done(): 183 return nil, errors.Errorf("request '%s' timeout", subject) 184 } 185 } 186 187 // Open starts the connection 188 func (conn *ConnectionMock) Open() error { 189 go conn.queueProcessing() 190 return nil 191 } 192 193 // Close destructs the connection 194 func (conn *ConnectionMock) Close() { 195 conn.queueShutdown <- true 196 } 197 198 // Check checks the connection 199 func (conn *ConnectionMock) Check() error { 200 return nil 201 } 202 203 // Servers returns list of currently connected servers 204 func (conn *ConnectionMock) Servers() []string { 205 return []string{"mockhost"} 206 } 207 208 func (conn *ConnectionMock) subscriptionAdd(subject string, handler nats.MsgHandler) { 209 subscriptions, exist := conn.subscriptions[subject] 210 if exist { 211 subscriptions = append(subscriptions, handler) 212 } else { 213 conn.subscriptions[subject] = []nats.MsgHandler{handler} 214 } 215 } 216 217 func (conn *ConnectionMock) subscriptionsGet(subject string) (*[]nats.MsgHandler, bool) { 218 subscriptions, exist := conn.subscriptions[subject] 219 return &subscriptions, exist 220 } 221 222 func (conn *ConnectionMock) queueProcessing() { 223 for { 224 select { 225 case <-conn.queueShutdown: 226 break 227 228 case message := <-conn.queue: 229 if subscriptions, exist := conn.subscriptionsGet(message.Subject); exist { 230 for _, handler := range *subscriptions { 231 go handler(message) 232 } 233 } 234 } 235 } 236 }