github.com/hyperledger/aries-framework-go@v0.3.2/pkg/client/messaging/client_test.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package messaging
     8  
     9  import (
    10  	"bytes"
    11  	"context"
    12  	"encoding/json"
    13  	"fmt"
    14  	"strings"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/stretchr/testify/require"
    19  
    20  	"github.com/hyperledger/aries-framework-go/component/storageutil/mem"
    21  	"github.com/hyperledger/aries-framework-go/pkg/common/model"
    22  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
    23  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/dispatcher"
    24  	"github.com/hyperledger/aries-framework-go/pkg/doc/did"
    25  	vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
    26  	"github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/msghandler"
    27  	"github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol"
    28  	"github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/generic"
    29  	mocksvc "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/service"
    30  	mockdiddoc "github.com/hyperledger/aries-framework-go/pkg/mock/diddoc"
    31  	mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms"
    32  	"github.com/hyperledger/aries-framework-go/pkg/mock/storage"
    33  	mockvdr "github.com/hyperledger/aries-framework-go/pkg/mock/vdr"
    34  	"github.com/hyperledger/aries-framework-go/pkg/store/connection"
    35  	spi "github.com/hyperledger/aries-framework-go/spi/storage"
    36  )
    37  
    38  func TestNew(t *testing.T) {
    39  	t.Run("Test create new command success", func(t *testing.T) {
    40  		client, err := New(&protocol.MockProvider{}, msghandler.NewMockMsgServiceProvider(), &mockNotifier{})
    41  		require.NoError(t, err)
    42  		require.NotNil(t, client)
    43  	})
    44  
    45  	t.Run("Test create new command success failure", func(t *testing.T) {
    46  		const errMsg = "sample-error"
    47  		client, err := New(&protocol.MockProvider{
    48  			StoreProvider: &storage.MockStoreProvider{
    49  				ErrOpenStoreHandle: fmt.Errorf(errMsg),
    50  			},
    51  		}, msghandler.NewMockMsgServiceProvider(), &mockNotifier{})
    52  		require.Error(t, err)
    53  		require.Nil(t, client)
    54  		require.Contains(t, err.Error(), errMsg)
    55  	})
    56  }
    57  
    58  func TestCommand_RegisterService(t *testing.T) {
    59  	t.Run("Successful Register Message Service", func(t *testing.T) {
    60  		msgRegistrar := msghandler.NewMockMsgServiceProvider()
    61  		cmd, err := New(&protocol.MockProvider{}, msgRegistrar, &mockNotifier{})
    62  		require.NoError(t, err)
    63  		require.NotNil(t, cmd)
    64  
    65  		err = cmd.RegisterService("json-msg-01", "https://didcomm.org/json/1.0/msg", "prp-01", "prp-02")
    66  		require.NoError(t, err)
    67  
    68  		// verify if new service is registered
    69  		require.NotEmpty(t, msgRegistrar.Services())
    70  		require.Equal(t, "json-msg-01", msgRegistrar.Services()[0].Name())
    71  		require.True(t, msgRegistrar.Services()[0].Accept(
    72  			"https://didcomm.org/json/1.0/msg",
    73  			[]string{"prp-01", "prp-02"},
    74  		))
    75  	})
    76  
    77  	t.Run("Register Message Service failure", func(t *testing.T) {
    78  		const errMsg = "sample-error"
    79  		mhandler := msghandler.NewMockMsgServiceProvider()
    80  		mhandler.RegisterErr = fmt.Errorf(errMsg)
    81  
    82  		cmd, err := New(&protocol.MockProvider{}, mhandler, &mockNotifier{})
    83  		require.NoError(t, err)
    84  		require.NotNil(t, cmd)
    85  
    86  		err = cmd.RegisterService("json-msg-01", "https://didcomm.org/json/1.0/msg", "prp-01", "prp-02")
    87  		require.Error(t, err)
    88  		require.Contains(t, err.Error(), errMsg)
    89  	})
    90  }
    91  
    92  func TestCommand_UnregisterService(t *testing.T) {
    93  	t.Run("Unregistering non existing message service", func(t *testing.T) {
    94  		cmd, err := New(&protocol.MockProvider{}, msghandler.NewMockMsgServiceProvider(), &mockNotifier{})
    95  		require.NoError(t, err)
    96  		require.NotNil(t, cmd)
    97  
    98  		err = cmd.UnregisterService("json-msg-01")
    99  		require.Error(t, err)
   100  	})
   101  
   102  	t.Run("Unregistering message service successfully", func(t *testing.T) {
   103  		msgRegistrar := msghandler.NewMockMsgServiceProvider()
   104  		cmd, err := New(&protocol.MockProvider{}, msgRegistrar, &mockNotifier{})
   105  		require.NoError(t, err)
   106  		require.NotNil(t, cmd)
   107  
   108  		svcNames := []string{"svc-01", "svc-02", "svc-03", "svc-04"}
   109  		for _, svcName := range svcNames {
   110  			err := msgRegistrar.Register(generic.NewCustomMockMessageSvc("test", svcName))
   111  			require.NoError(t, err)
   112  		}
   113  
   114  		require.Len(t, msgRegistrar.Services(), 4)
   115  
   116  		for _, svcName := range svcNames {
   117  			err := cmd.UnregisterService(svcName)
   118  			require.NoError(t, err)
   119  		}
   120  
   121  		require.Len(t, msgRegistrar.Services(), 0)
   122  	})
   123  }
   124  
   125  func TestCommand_Services(t *testing.T) {
   126  	msgRegistrar := msghandler.NewMockMsgServiceProvider()
   127  	cmd, err := New(&protocol.MockProvider{}, msgRegistrar, &mockNotifier{})
   128  	require.NoError(t, err)
   129  	require.NotNil(t, cmd)
   130  
   131  	svcs := cmd.Services()
   132  	require.Empty(t, svcs)
   133  
   134  	testMsgSvcs := []dispatcher.MessageService{
   135  		generic.NewCustomMockMessageSvc("type-01", "svc-name-01"),
   136  		generic.NewCustomMockMessageSvc("type-02", "svc-name-02"),
   137  		generic.NewCustomMockMessageSvc("type-03", "svc-name-03"),
   138  		generic.NewCustomMockMessageSvc("type-04", "svc-name-04"),
   139  		generic.NewCustomMockMessageSvc("type-05", "svc-name-05"),
   140  	}
   141  	err = msgRegistrar.Register(testMsgSvcs...)
   142  	require.NoError(t, err)
   143  
   144  	svcs = cmd.Services()
   145  	require.Len(t, svcs, len(testMsgSvcs))
   146  }
   147  
   148  func TestCommand_Send(t *testing.T) { // nolint: gocognit, gocyclo
   149  	t.Run("Test send new message success", func(t *testing.T) {
   150  		tests := []struct {
   151  			name           string
   152  			testConnection *connection.Record
   153  			msgBody        string
   154  			option         SendMessageOpions
   155  		}{
   156  			{
   157  				name: "send message to connection ID",
   158  				testConnection: &connection.Record{
   159  					ConnectionID: "sample-conn-ID-001",
   160  					State:        "completed", MyDID: "mydid", TheirDID: "theirDID-001",
   161  				},
   162  				option: SendByConnectionID("sample-conn-ID-001"),
   163  			},
   164  			{
   165  				name: "send message to their DID",
   166  				testConnection: &connection.Record{
   167  					ConnectionID: "sample-conn-ID-001",
   168  					State:        "completed", MyDID: "mydid", TheirDID: "theirDID-001",
   169  				},
   170  				option: SendByTheirDID("theirDID-001"),
   171  			},
   172  			{
   173  				name: "send message to destination",
   174  				option: SendByDestination(&service.Destination{
   175  					RecipientKeys:   []string{"test"},
   176  					ServiceEndpoint: model.NewDIDCommV1Endpoint("dfsdf"),
   177  					RoutingKeys:     []string{"test"},
   178  				}),
   179  			},
   180  		}
   181  
   182  		// Note: copied from store/connection/connection_lookup.go
   183  		mockDIDTagFunc := func(dids ...string) string {
   184  			for i, v := range dids {
   185  				dids[i] = strings.ReplaceAll(v, ":", "$")
   186  			}
   187  
   188  			return strings.Join(dids, "|")
   189  		}
   190  
   191  		t.Parallel()
   192  
   193  		for _, test := range tests {
   194  			tc := test
   195  			t.Run(tc.name, func(t *testing.T) {
   196  				memProvider := mem.NewProvider()
   197  
   198  				memStore, err := memProvider.OpenStore("didexchange")
   199  				require.NoError(t, err)
   200  
   201  				if tc.testConnection != nil {
   202  					connBytes, errMarshal := json.Marshal(tc.testConnection)
   203  					require.NoError(t, errMarshal)
   204  					require.NoError(t,
   205  						memStore.Put(fmt.Sprintf("conn_%s", tc.testConnection.ConnectionID), connBytes,
   206  							spi.Tag{Name: "conn_"},
   207  							spi.Tag{Name: "bothDIDs", Value: mockDIDTagFunc(tc.testConnection.MyDID, tc.testConnection.TheirDID)},
   208  							spi.Tag{Name: "theirDID", Value: mockDIDTagFunc(tc.testConnection.TheirDID)},
   209  						))
   210  				}
   211  
   212  				cmd, err := New(&protocol.MockProvider{
   213  					StoreProvider:              memProvider,
   214  					ProtocolStateStoreProvider: mem.NewProvider(),
   215  				},
   216  					msghandler.NewMockMsgServiceProvider(), &mockNotifier{})
   217  				require.NoError(t, err)
   218  				require.NotNil(t, cmd)
   219  
   220  				var b bytes.Buffer
   221  				res, err := cmd.Send(json.RawMessage([]byte(`{"text":"sample"}`)), tc.option)
   222  				require.NoError(t, err)
   223  				require.Empty(t, b.String())
   224  				require.Empty(t, res)
   225  			})
   226  		}
   227  	})
   228  
   229  	const msgStr = `{"@id": "2d798168-8abf-4410-8535-bc1e8406a5ff","text":"sample"}`
   230  
   231  	const replyMsgStr = `{
   232  							"@id": "123456781",
   233  							"@type": "sample-response-type",
   234  							"~thread" : {"thid": "2d798168-8abf-4410-8535-bc1e8406a5ff"}
   235  					}`
   236  
   237  	t.Run("Test send new message and await response success", func(t *testing.T) {
   238  		tests := []struct {
   239  			name           string
   240  			testConnection *connection.Record
   241  			msgBody        string
   242  			option         []SendMessageOpions
   243  		}{
   244  			{
   245  				name: "send message to connection ID",
   246  				testConnection: &connection.Record{
   247  					ConnectionID: "sample-conn-ID-001",
   248  					State:        "completed", MyDID: "mydid", TheirDID: "theirDID-001",
   249  				},
   250  				option: []SendMessageOpions{
   251  					SendByConnectionID("sample-conn-ID-001"),
   252  					WaitForResponse(context.Background(), "sample-response-type"),
   253  				},
   254  			},
   255  			{
   256  				name: "send message to their DID",
   257  				testConnection: &connection.Record{
   258  					ConnectionID: "sample-conn-ID-001",
   259  					State:        "completed", MyDID: "mydid", TheirDID: "theirDID-001",
   260  				},
   261  				option: []SendMessageOpions{
   262  					SendByTheirDID("theirDID-001"),
   263  					WaitForResponse(context.Background(), "sample-response-type"),
   264  				},
   265  			},
   266  			{
   267  				name: "send message to destination",
   268  				option: []SendMessageOpions{
   269  					SendByDestination(&service.Destination{
   270  						RecipientKeys:   []string{"test"},
   271  						ServiceEndpoint: model.NewDIDCommV1Endpoint("sdfsdf"),
   272  						RoutingKeys:     []string{"test"},
   273  					}),
   274  					WaitForResponse(context.Background(), "sample-response-type"),
   275  				},
   276  			},
   277  		}
   278  
   279  		// Note: copied from store/connection/connection_lookup.go
   280  		mockDIDTagFunc := func(dids ...string) string {
   281  			for i, v := range dids {
   282  				dids[i] = strings.ReplaceAll(v, ":", "$")
   283  			}
   284  
   285  			return strings.Join(dids, "|")
   286  		}
   287  
   288  		t.Parallel()
   289  
   290  		for _, test := range tests {
   291  			tc := test
   292  			t.Run(tc.name, func(t *testing.T) {
   293  				memProvider := mem.NewProvider()
   294  
   295  				memStore, err := memProvider.OpenStore("didexchange")
   296  				require.NoError(t, err)
   297  
   298  				if tc.testConnection != nil {
   299  					connBytes, errMarshal := json.Marshal(tc.testConnection)
   300  					require.NoError(t, errMarshal)
   301  					require.NoError(t,
   302  						memStore.Put(fmt.Sprintf("conn_%s", tc.testConnection.ConnectionID), connBytes,
   303  							spi.Tag{Name: "conn_"},
   304  							spi.Tag{Name: "bothDIDs", Value: mockDIDTagFunc(tc.testConnection.MyDID, tc.testConnection.TheirDID)},
   305  							spi.Tag{Name: "theirDID", Value: mockDIDTagFunc(tc.testConnection.TheirDID)},
   306  						))
   307  				}
   308  
   309  				registrar := msghandler.NewMockMsgServiceProvider()
   310  
   311  				cmd, err := New(&protocol.MockProvider{
   312  					StoreProvider:              memProvider,
   313  					ProtocolStateStoreProvider: mem.NewProvider(),
   314  				},
   315  					registrar, &mockNotifier{})
   316  				require.NoError(t, err)
   317  				require.NotNil(t, cmd)
   318  
   319  				replyMsg, err := service.ParseDIDCommMsgMap([]byte(replyMsgStr))
   320  				require.NoError(t, err)
   321  
   322  				go func() {
   323  					for {
   324  						services := registrar.Services()
   325  						if len(services) > 0 {
   326  							_, e := services[0].HandleInbound(replyMsg, service.NewDIDCommContext("sampleDID", "sampleTheirDID", nil))
   327  							require.NoError(t, e)
   328  
   329  							break
   330  						}
   331  					}
   332  				}()
   333  
   334  				res, err := cmd.Send(
   335  					json.RawMessage(msgStr),
   336  					tc.option...)
   337  				require.NoError(t, err)
   338  
   339  				var response map[string]interface{}
   340  				err = json.Unmarshal(res, &response)
   341  				require.NoError(t, err)
   342  				require.NotEmpty(t, response)
   343  				require.NotEmpty(t, response["message"])
   344  			})
   345  		}
   346  	})
   347  
   348  	t.Run("Test send new message failures with bad context", func(t *testing.T) {
   349  		badContext, cancel := context.WithTimeout(context.Background(), 0*time.Second)
   350  		defer cancel()
   351  
   352  		connBytes, err := json.Marshal(&connection.Record{
   353  			ConnectionID: "sample-conn-ID-001",
   354  			State:        "completed", MyDID: "mydid", TheirDID: "theirDID-001",
   355  		})
   356  		require.NoError(t, err)
   357  
   358  		mockStore := &storage.MockStore{Store: make(map[string]storage.DBEntry)}
   359  		require.NoError(t, mockStore.Put("conn_sample-conn-ID-001", connBytes))
   360  
   361  		cmd, err := New(&protocol.MockProvider{StoreProvider: storage.NewCustomMockStoreProvider(mockStore)},
   362  			msghandler.NewMockMsgServiceProvider(), &mockNotifier{})
   363  		require.NoError(t, err)
   364  		require.NotNil(t, cmd)
   365  
   366  		res, err := cmd.Send(
   367  			json.RawMessage([]byte(msgStr)),
   368  			SendByConnectionID("sample-conn-ID-001"),
   369  			WaitForResponse(badContext, "sample-rep-type"),
   370  		)
   371  		require.Error(t, err)
   372  		require.Contains(t, err.Error(), "failed to get reply, context deadline exceeded")
   373  		require.Empty(t, res)
   374  	})
   375  
   376  	t.Run("Test send new message failures", func(t *testing.T) {
   377  		tests := []struct {
   378  			name           string
   379  			testConnection *connection.Record
   380  			messenger      *mocksvc.MockMessenger
   381  			kms            *mockkms.KeyManager
   382  			vdr            *mockvdr.MockVDRegistry
   383  			option         SendMessageOpions
   384  			errorMsg       string
   385  			msgBody        json.RawMessage
   386  		}{
   387  			{
   388  				name:           "send message to connection ID data not found error",
   389  				testConnection: nil,
   390  				errorMsg:       "data not found",
   391  				option:         SendByConnectionID("sample-conn-ID-001"),
   392  			},
   393  			{
   394  				name:           "send message without any options",
   395  				testConnection: nil,
   396  				errorMsg:       "missing message destination",
   397  				option:         SendByConnectionID(""),
   398  			},
   399  			{
   400  				name: "send message to connection ID send error",
   401  				testConnection: &connection.Record{
   402  					ConnectionID: "sample-conn-ID-001",
   403  					State:        "completed", MyDID: "mydid", TheirDID: "theirDID-001",
   404  				},
   405  				option:    SendByConnectionID("sample-conn-ID-001"),
   406  				messenger: &mocksvc.MockMessenger{ErrSend: fmt.Errorf("sample-err-01")},
   407  				errorMsg:  "sample-err-01",
   408  			},
   409  			{
   410  				name: "send message to their DID data not found error",
   411  				testConnection: &connection.Record{
   412  					ConnectionID: "sample-conn-ID-001",
   413  					State:        "completed", MyDID: "mydid", TheirDID: "theirDID-z",
   414  				},
   415  				option:   SendByTheirDID("theirDID-001"),
   416  				errorMsg: vdrapi.ErrNotFound.Error(),
   417  			},
   418  			{
   419  				name: "send message to destination - failure 1",
   420  				option: SendByDestination(&service.Destination{
   421  					RecipientKeys:   []string{"test"},
   422  					ServiceEndpoint: model.NewDIDCommV1Endpoint("sdfsdf"),
   423  					RoutingKeys:     []string{"test"},
   424  				}),
   425  				messenger: &mocksvc.MockMessenger{ErrSendToDestination: fmt.Errorf("sample-err-01")},
   426  				errorMsg:  "sample-err-01",
   427  			},
   428  			{
   429  				name: "send message to destination - failure 2",
   430  				kms:  &mockkms.KeyManager{CrAndExportPubKeyErr: fmt.Errorf("sample-kmserr-01")},
   431  				option: SendByDestination(&service.Destination{
   432  					RecipientKeys:   []string{"test"},
   433  					ServiceEndpoint: model.NewDIDCommV1Endpoint("sdfsdf"),
   434  					RoutingKeys:     []string{"test"},
   435  				}),
   436  				errorMsg: "sample-kmserr-01",
   437  			},
   438  			{
   439  				name:     "failed to resolve destination from DID",
   440  				option:   SendByTheirDID("theirDID-001"),
   441  				vdr:      &mockvdr.MockVDRegistry{ResolveErr: fmt.Errorf("sample-err-01")},
   442  				errorMsg: "sample-err-01",
   443  			},
   444  			{
   445  				name:   "invalid message body - scenario 1",
   446  				option: SendByTheirDID("theirDID-001"),
   447  				vdr: &mockvdr.MockVDRegistry{
   448  					ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (doc *did.DocResolution, e error) {
   449  						return &did.DocResolution{DIDDocument: mockdiddoc.GetMockDIDDoc(t, false)}, nil
   450  					},
   451  				},
   452  				errorMsg: "invalid payload data format",
   453  				msgBody:  json.RawMessage([]byte("--")),
   454  			},
   455  			{
   456  				name: "invalid message body - scenario 2",
   457  				testConnection: &connection.Record{
   458  					ConnectionID: "sample-conn-ID-001",
   459  					State:        "completed", MyDID: "mydid", TheirDID: "theirDID-001",
   460  				},
   461  				option:   SendByConnectionID("sample-conn-ID-001"),
   462  				errorMsg: "invalid payload data format",
   463  				msgBody:  json.RawMessage([]byte("--")),
   464  			},
   465  		}
   466  
   467  		t.Parallel()
   468  
   469  		for _, test := range tests {
   470  			tc := test
   471  			t.Run(tc.name, func(t *testing.T) {
   472  				provider := &protocol.MockProvider{ProtocolStateStoreProvider: mem.NewProvider()}
   473  
   474  				memProvider := mem.NewProvider()
   475  
   476  				memStore, err := memProvider.OpenStore("didexchange")
   477  				require.NoError(t, err)
   478  
   479  				if tc.testConnection != nil {
   480  					connBytes, errMarshal := json.Marshal(tc.testConnection)
   481  					require.NoError(t, errMarshal)
   482  
   483  					require.NoError(t, memStore.Put(fmt.Sprintf("conn_%s", tc.testConnection.ConnectionID), connBytes))
   484  				}
   485  
   486  				provider.StoreProvider = memProvider
   487  
   488  				if tc.messenger != nil {
   489  					provider.CustomMessenger = tc.messenger
   490  				}
   491  
   492  				if tc.kms != nil {
   493  					provider.CustomKMS = tc.kms
   494  				}
   495  
   496  				if tc.vdr != nil {
   497  					provider.CustomVDR = tc.vdr
   498  				}
   499  
   500  				cmd, err := New(provider, msghandler.NewMockMsgServiceProvider(), &mockNotifier{})
   501  				require.NoError(t, err)
   502  				require.NotNil(t, cmd)
   503  
   504  				msgBody := json.RawMessage([]byte(`{"text":"sample"}`))
   505  
   506  				if tc.msgBody != nil {
   507  					msgBody = tc.msgBody
   508  				}
   509  
   510  				res, err := cmd.Send(msgBody, tc.option)
   511  				require.Error(t, err, "failed test : %s", tc.name)
   512  				require.Contains(t, err.Error(), tc.errorMsg)
   513  				require.Empty(t, res)
   514  			})
   515  		}
   516  	})
   517  }
   518  
   519  func TestCommand_Reply(t *testing.T) {
   520  	t.Run("Test reply validation and failures", func(t *testing.T) {
   521  		tests := []struct {
   522  			name      string
   523  			msgBody   string
   524  			msgID     string
   525  			messenger *mocksvc.MockMessenger
   526  			errorMsg  string
   527  		}{
   528  			{
   529  				name:     "invalid message format",
   530  				msgBody:  `"sample-msg"`,
   531  				errorMsg: "invalid payload data format",
   532  			},
   533  			{
   534  				name:      "message reply error",
   535  				msgBody:   `{"msg":"Hello !!"}`,
   536  				messenger: &mocksvc.MockMessenger{ErrReplyTo: fmt.Errorf("sample-err-01")},
   537  				errorMsg:  "sample-err-01",
   538  			},
   539  		}
   540  
   541  		t.Parallel()
   542  
   543  		for _, test := range tests {
   544  			tc := test
   545  			t.Run(tc.name, func(t *testing.T) {
   546  				provider := &protocol.MockProvider{}
   547  
   548  				if tc.messenger != nil {
   549  					provider.CustomMessenger = tc.messenger
   550  				}
   551  
   552  				cmd, err := New(provider, msghandler.NewMockMsgServiceProvider(), &mockNotifier{})
   553  				require.NoError(t, err)
   554  				require.NotNil(t, cmd)
   555  
   556  				res, err := cmd.Reply(context.Background(), json.RawMessage([]byte(tc.msgBody)), tc.msgID, false, "")
   557  				require.Error(t, err)
   558  				require.Contains(t, err.Error(), tc.errorMsg)
   559  				require.Empty(t, res)
   560  			})
   561  		}
   562  	})
   563  
   564  	t.Run("Test send message reply", func(t *testing.T) {
   565  		cmd, err := New(&protocol.MockProvider{}, msghandler.NewMockMsgServiceProvider(), &mockNotifier{})
   566  		require.NoError(t, err)
   567  		require.NotNil(t, cmd)
   568  
   569  		res, err := cmd.Reply(context.Background(), json.RawMessage([]byte(`{"msg":"Hello !!"}`)), "msg-id", false, "")
   570  		require.NoError(t, err)
   571  		require.Empty(t, res)
   572  	})
   573  
   574  	t.Run("Test send message reply by starting new thread", func(t *testing.T) {
   575  		cmd, err := New(&protocol.MockProvider{}, msghandler.NewMockMsgServiceProvider(), &mockNotifier{})
   576  		require.NoError(t, err)
   577  		require.NotNil(t, cmd)
   578  
   579  		res, err := cmd.Reply(context.Background(), json.RawMessage([]byte(`{"msg":"Hello !!"}`)), "msg-id", true, "")
   580  		require.NoError(t, err)
   581  		require.Empty(t, res)
   582  	})
   583  
   584  	const msgStr = `{"@id": "2d798168-8abf-4410-8535-bc1e8406a5ff","text":"sample"}`
   585  
   586  	const replyMsgStr = `{
   587  							"@id": "123456781",
   588  							"@type": "sample-response-type",
   589  							"~thread" : {"thid": "2d798168-8abf-4410-8535-bc1e8406a5ff"}
   590  					}`
   591  
   592  	t.Run("Test send message reply and await response", func(t *testing.T) {
   593  		registrar := msghandler.NewMockMsgServiceProvider()
   594  
   595  		cmd, err := New(&protocol.MockProvider{}, registrar, &mockNotifier{})
   596  		require.NoError(t, err)
   597  		require.NotNil(t, cmd)
   598  
   599  		replyMsg, err := service.ParseDIDCommMsgMap([]byte(replyMsgStr))
   600  		require.NoError(t, err)
   601  
   602  		go func() {
   603  			for {
   604  				services := registrar.Services()
   605  
   606  				if len(services) > 0 {
   607  					_, e := services[0].HandleInbound(replyMsg, service.NewDIDCommContext("sampleDID", "sampleTheirDID", nil))
   608  					require.NoError(t, e)
   609  				}
   610  			}
   611  		}()
   612  
   613  		res, err := cmd.Reply(context.Background(), json.RawMessage([]byte(msgStr)), "msg-id", false, "sample-response-type")
   614  		require.NoError(t, err)
   615  
   616  		var response map[string]interface{}
   617  		err = json.Unmarshal(res, &response)
   618  		require.NoError(t, err)
   619  		require.NotEmpty(t, response)
   620  		require.NotEmpty(t, response["message"])
   621  	})
   622  
   623  	t.Run("Test send message reply by starting new thread  and await response", func(t *testing.T) {
   624  		registrar := msghandler.NewMockMsgServiceProvider()
   625  
   626  		cmd, err := New(&protocol.MockProvider{}, registrar, &mockNotifier{})
   627  		require.NoError(t, err)
   628  		require.NotNil(t, cmd)
   629  
   630  		replyMsg, err := service.ParseDIDCommMsgMap([]byte(replyMsgStr))
   631  		require.NoError(t, err)
   632  
   633  		go func() {
   634  			for {
   635  				svcs := registrar.Services()
   636  				if len(svcs) > 0 {
   637  					_, e := svcs[0].HandleInbound(
   638  						replyMsg, service.NewDIDCommContext("sampleDID", "sampleTheirDID", nil))
   639  					require.NoError(t, e)
   640  				}
   641  			}
   642  		}()
   643  
   644  		res, err := cmd.Reply(context.Background(), json.RawMessage([]byte(msgStr)), "msg-id", true, "sample-response-type")
   645  		require.NoError(t, err)
   646  
   647  		var response map[string]interface{}
   648  		err = json.Unmarshal(res, &response)
   649  		require.NoError(t, err)
   650  		require.NotEmpty(t, response)
   651  		require.NotEmpty(t, response["message"])
   652  	})
   653  }
   654  
   655  // mockNotifier is mock implementation of Notifier.
   656  type mockNotifier struct {
   657  	NotifyFunc func(topic string, message []byte) error
   658  }
   659  
   660  // Notify is mock implementation of Notifier Notify().
   661  func (n *mockNotifier) Notify(topic string, message []byte) error {
   662  	if n.NotifyFunc != nil {
   663  		return n.NotifyFunc(topic, message)
   664  	}
   665  
   666  	return nil
   667  }