github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/dispatcher/outbound/outbound_test.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package outbound
     8  
     9  import (
    10  	"encoding/json"
    11  	"errors"
    12  	"fmt"
    13  	"testing"
    14  
    15  	"github.com/google/uuid"
    16  	"github.com/stretchr/testify/mock"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/hyperledger/aries-framework-go/pkg/common/model"
    20  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/middleware"
    21  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
    22  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator"
    23  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/transport"
    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/kms"
    27  	mockdidcomm "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm"
    28  	mockpackager "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/packager"
    29  	mockdiddoc "github.com/hyperledger/aries-framework-go/pkg/mock/diddoc"
    30  	mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms"
    31  	mockstore "github.com/hyperledger/aries-framework-go/pkg/mock/storage"
    32  	mockvdr "github.com/hyperledger/aries-framework-go/pkg/mock/vdr"
    33  	"github.com/hyperledger/aries-framework-go/pkg/store/connection"
    34  	"github.com/hyperledger/aries-framework-go/spi/storage"
    35  )
    36  
    37  func TestNewOutbound(t *testing.T) {
    38  	t.Run("error if cannot init connection lookup", func(t *testing.T) {
    39  		expected := errors.New("test")
    40  		_, err := NewOutbound(&mockProvider{
    41  			protoStorageProvider: mockstore.NewMockStoreProvider(),
    42  			storageProvider: &mockstore.MockStoreProvider{
    43  				ErrOpenStoreHandle: expected,
    44  			},
    45  			mediaTypeProfiles: []string{transport.MediaTypeDIDCommV2Profile},
    46  		})
    47  		require.ErrorIs(t, err, expected)
    48  	})
    49  }
    50  
    51  func TestOutBoundDispatcher_createPackedNestedForwards(t *testing.T) {
    52  	t.Run("test send with nested forward message - success", func(t *testing.T) {
    53  		data := "data"
    54  		recKey1 := "recKey1"
    55  		rtKey1 := "rtKey1"
    56  		rtKey2 := "rtKey2"
    57  		packager := &mockPackager{}
    58  		expectedRequest := `{"protected":"","iv":"","ciphertext":"","tag":""}`
    59  
    60  		o, err := NewOutbound(&mockProvider{
    61  			packagerValue:           packager,
    62  			outboundTransportsValue: []transport.OutboundTransport{&mockOutboundTransport{expectedRequest: expectedRequest}},
    63  			storageProvider:         mockstore.NewMockStoreProvider(),
    64  			protoStorageProvider:    mockstore.NewMockStoreProvider(),
    65  			mediaTypeProfiles:       []string{transport.MediaTypeDIDCommV2Profile},
    66  		})
    67  		require.NoError(t, err)
    68  
    69  		packager.On("PackMessage", []string{recKey1}).Return([]byte(expectedRequest))
    70  		packager.On("PackMessage", []string{rtKey1}).Return([]byte(expectedRequest))
    71  		packager.On("PackMessage", []string{rtKey2}).Return([]byte(expectedRequest))
    72  
    73  		require.NoError(t, o.Send(data, "", &service.Destination{
    74  			ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{
    75  				{URI: "url", RoutingKeys: []string{rtKey1, rtKey2}},
    76  			}),
    77  			RecipientKeys: []string{recKey1},
    78  		}))
    79  		packager.AssertExpectations(t)
    80  	})
    81  }
    82  
    83  func TestOutboundDispatcher_Send(t *testing.T) {
    84  	t.Run("test success", func(t *testing.T) {
    85  		o, err := NewOutbound(&mockProvider{
    86  			packagerValue:           &mockpackager.Packager{},
    87  			outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: true}},
    88  			storageProvider:         mockstore.NewMockStoreProvider(),
    89  			protoStorageProvider:    mockstore.NewMockStoreProvider(),
    90  			mediaTypeProfiles:       []string{transport.MediaTypeV1PlaintextPayload},
    91  		})
    92  		require.NoError(t, err)
    93  		require.NoError(t, o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{
    94  			ServiceEndpoint: model.NewDIDCommV1Endpoint("url"),
    95  		}))
    96  	})
    97  
    98  	t.Run("test success", func(t *testing.T) {
    99  		o, err := NewOutbound(&mockProvider{
   100  			packagerValue:           &mockpackager.Packager{},
   101  			outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: true}},
   102  			storageProvider:         mockstore.NewMockStoreProvider(),
   103  			protoStorageProvider:    mockstore.NewMockStoreProvider(),
   104  			mediaTypeProfiles:       []string{transport.MediaTypeDIDCommV2Profile},
   105  		})
   106  		require.NoError(t, err)
   107  
   108  		fromDIDDoc := mockdiddoc.GetMockDIDDocWithDIDCommV2Bloc(t, "alice")
   109  		toDIDDoc := mockdiddoc.GetMockDIDDocWithDIDCommV2Bloc(t, "bob")
   110  
   111  		require.NoError(t, o.Send("data", fromDIDDoc.KeyAgreement[0].VerificationMethod.ID, &service.Destination{
   112  			RecipientKeys: []string{toDIDDoc.KeyAgreement[0].VerificationMethod.ID},
   113  			ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{
   114  				URI:    "url",
   115  				Accept: []string{transport.MediaTypeDIDCommV2Profile},
   116  			}}),
   117  		}))
   118  	})
   119  
   120  	t.Run("test no outbound transport found", func(t *testing.T) {
   121  		o, err := NewOutbound(&mockProvider{
   122  			packagerValue:           &mockpackager.Packager{},
   123  			outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: false}},
   124  			storageProvider:         mockstore.NewMockStoreProvider(),
   125  			protoStorageProvider:    mockstore.NewMockStoreProvider(),
   126  			mediaTypeProfiles:       []string{transport.MediaTypeDIDCommV2Profile},
   127  		})
   128  		require.NoError(t, err)
   129  		err = o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{
   130  			ServiceEndpoint: model.NewDIDCommV1Endpoint("url"),
   131  		})
   132  		require.Error(t, err)
   133  		require.Contains(t, err.Error(), "outboundDispatcher.Send: no transport found for destination")
   134  	})
   135  
   136  	t.Run("test pack msg failure", func(t *testing.T) {
   137  		o, err := NewOutbound(&mockProvider{
   138  			packagerValue:           &mockpackager.Packager{PackErr: fmt.Errorf("pack error")},
   139  			outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: true}},
   140  			storageProvider:         mockstore.NewMockStoreProvider(),
   141  			protoStorageProvider:    mockstore.NewMockStoreProvider(),
   142  			mediaTypeProfiles:       []string{transport.MediaTypeDIDCommV2Profile},
   143  		})
   144  		require.NoError(t, err)
   145  		err = o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{
   146  			ServiceEndpoint: model.NewDIDCommV1Endpoint("url"),
   147  		})
   148  		require.Error(t, err)
   149  		require.Contains(t, err.Error(), "pack error")
   150  	})
   151  
   152  	t.Run("test outbound send failure", func(t *testing.T) {
   153  		o, err := NewOutbound(&mockProvider{
   154  			packagerValue: &mockpackager.Packager{},
   155  			outboundTransportsValue: []transport.OutboundTransport{
   156  				&mockdidcomm.MockOutboundTransport{AcceptValue: true, SendErr: fmt.Errorf("send error")},
   157  			},
   158  			storageProvider:      mockstore.NewMockStoreProvider(),
   159  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   160  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   161  		})
   162  		require.NoError(t, err)
   163  		err = o.Send("data", mockdiddoc.MockDIDKey(t),
   164  			&service.Destination{ServiceEndpoint: model.NewDIDCommV1Endpoint("url")})
   165  		require.Error(t, err)
   166  		require.Contains(t, err.Error(), "send error")
   167  	})
   168  
   169  	t.Run("test send with forward message - success", func(t *testing.T) {
   170  		o, err := NewOutbound(&mockProvider{
   171  			packagerValue:           &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   172  			outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: true}},
   173  			storageProvider:         mockstore.NewMockStoreProvider(),
   174  			protoStorageProvider:    mockstore.NewMockStoreProvider(),
   175  			mediaTypeProfiles:       []string{transport.MediaTypeDIDCommV2Profile},
   176  		})
   177  		require.NoError(t, err)
   178  
   179  		require.NoError(t, o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{
   180  			ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{
   181  				{URI: "url", RoutingKeys: []string{"xyz"}},
   182  			}),
   183  			RecipientKeys: []string{"abc"},
   184  		}))
   185  	})
   186  
   187  	t.Run("test send with forward message with multiple media type profiles- success", func(t *testing.T) {
   188  		o, err := NewOutbound(&mockProvider{
   189  			packagerValue:           &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   190  			outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: true}},
   191  			storageProvider:         mockstore.NewMockStoreProvider(),
   192  			protoStorageProvider:    mockstore.NewMockStoreProvider(),
   193  			mediaTypeProfiles: []string{
   194  				transport.MediaTypeProfileDIDCommAIP1,
   195  				transport.MediaTypeAIP2RFC0019Profile,
   196  				transport.MediaTypeDIDCommV2Profile,
   197  			},
   198  		})
   199  		require.NoError(t, err)
   200  
   201  		require.NoError(t, o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{
   202  			ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{
   203  				{URI: "url", RoutingKeys: []string{"xyz"}},
   204  			}),
   205  			RecipientKeys: []string{"abc"},
   206  		}))
   207  	})
   208  
   209  	t.Run("test send with forward message - packer error", func(t *testing.T) {
   210  		o, err := NewOutbound(&mockProvider{
   211  			packagerValue:           &mockpackager.Packager{PackErr: errors.New("pack error")},
   212  			outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: true}},
   213  			storageProvider:         mockstore.NewMockStoreProvider(),
   214  			protoStorageProvider:    mockstore.NewMockStoreProvider(),
   215  			mediaTypeProfiles:       []string{transport.MediaTypeDIDCommV2Profile},
   216  		})
   217  		require.NoError(t, err)
   218  
   219  		_, err = o.createForwardMessage(createPackedMsgForForward(t), &service.Destination{
   220  			ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{
   221  				{URI: "url", RoutingKeys: []string{"xyz"}},
   222  			}),
   223  			RecipientKeys: []string{"abc"},
   224  		})
   225  		require.Error(t, err)
   226  		require.Contains(t, err.Error(), "pack forward msg")
   227  	})
   228  
   229  	t.Run("test send with legacy forward message - success", func(t *testing.T) {
   230  		o, err := NewOutbound(&mockProvider{
   231  			packagerValue:           &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   232  			outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: true}},
   233  			storageProvider:         mockstore.NewMockStoreProvider(),
   234  			protoStorageProvider:    mockstore.NewMockStoreProvider(),
   235  			mediaTypeProfiles:       []string{transport.LegacyDIDCommV1Profile},
   236  		})
   237  		require.NoError(t, err)
   238  
   239  		require.NoError(t, o.Send("data", mockdiddoc.MockDIDKey(t), &service.Destination{
   240  			ServiceEndpoint: model.NewDIDCommV1Endpoint("url"),
   241  			RecipientKeys:   []string{"abc"},
   242  		}))
   243  	})
   244  
   245  	t.Run("test send with legacy forward message - did:key error", func(t *testing.T) {
   246  		o, err := NewOutbound(&mockProvider{
   247  			packagerValue:           &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   248  			outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: true}},
   249  			storageProvider:         mockstore.NewMockStoreProvider(),
   250  			protoStorageProvider:    mockstore.NewMockStoreProvider(),
   251  			mediaTypeProfiles:       []string{transport.LegacyDIDCommV1Profile},
   252  		})
   253  		require.NoError(t, err)
   254  
   255  		env := []byte(`{"protected": "-", "iv": "-", "ciphertext": "-", "tag": "-"}`)
   256  		_, err = o.createForwardMessage(env, &service.Destination{
   257  			ServiceEndpoint: model.NewDIDCommV1Endpoint("url"),
   258  			RecipientKeys:   []string{"did:key:invalid"},
   259  			RoutingKeys:     []string{"did:key:invalid"},
   260  		})
   261  		require.Error(t, err)
   262  		require.Contains(t, err.Error(), "GetBase58PubKeyFromDIDKey: failed to parse public key bytes from")
   263  	})
   264  }
   265  
   266  const testDID = "did:test:abc"
   267  
   268  type mockMessage struct {
   269  	Type string
   270  }
   271  
   272  func TestOutboundDispatcher_SendToDID(t *testing.T) {
   273  	mockDoc := mockdiddoc.GetMockDIDDoc(t, false)
   274  
   275  	t.Run("success with existing connection record", func(t *testing.T) {
   276  		o, err := NewOutbound(&mockProvider{
   277  			packagerValue: &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   278  			vdr: &mockvdr.MockVDRegistry{
   279  				ResolveValue: mockDoc,
   280  			},
   281  			outboundTransportsValue: []transport.OutboundTransport{
   282  				&mockdidcomm.MockOutboundTransport{AcceptValue: true},
   283  			},
   284  			storageProvider:      mockstore.NewMockStoreProvider(),
   285  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   286  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   287  		})
   288  		require.NoError(t, err)
   289  
   290  		o.connections = &mockConnectionLookup{
   291  			getConnectionByDIDsVal: "mock1",
   292  			getConnectionRecordVal: &connection.Record{},
   293  		}
   294  
   295  		require.NoError(t, o.SendToDID(service.DIDCommMsgMap{
   296  			"@id":   "123",
   297  			"@type": "abc",
   298  		}, testDID, ""))
   299  	})
   300  
   301  	t.Run("success with did rotation check", func(t *testing.T) {
   302  		o, err := NewOutbound(&mockProvider{
   303  			packagerValue: &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   304  			vdr: &mockvdr.MockVDRegistry{
   305  				ResolveValue: mockDoc,
   306  			},
   307  			outboundTransportsValue: []transport.OutboundTransport{
   308  				&mockdidcomm.MockOutboundTransport{AcceptValue: true},
   309  			},
   310  			storageProvider:      mockstore.NewMockStoreProvider(),
   311  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   312  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   313  			didRotator:           middleware.DIDCommMessageMiddleware{},
   314  		})
   315  		require.NoError(t, err)
   316  
   317  		o.connections = &mockConnectionLookup{
   318  			getConnectionByDIDsVal: "mock1",
   319  			getConnectionRecordVal: &connection.Record{
   320  				PeerDIDInitialState: "mock-peer-initial-state",
   321  				DIDCommVersion:      service.V2,
   322  				ParentThreadID:      "parent-thread-id-value",
   323  			},
   324  		}
   325  
   326  		require.NoError(t, o.SendToDID(service.DIDCommMsgMap{
   327  			"id":   "123",
   328  			"type": "abc",
   329  			"thid": "123",
   330  		}, testDID, ""))
   331  	})
   332  
   333  	t.Run("did rotation err", func(t *testing.T) {
   334  		o, err := NewOutbound(&mockProvider{
   335  			packagerValue: &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   336  			vdr: &mockvdr.MockVDRegistry{
   337  				ResolveValue: mockDoc,
   338  			},
   339  			outboundTransportsValue: []transport.OutboundTransport{
   340  				&mockdidcomm.MockOutboundTransport{AcceptValue: true},
   341  			},
   342  			storageProvider:      mockstore.NewMockStoreProvider(),
   343  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   344  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   345  			didRotator:           middleware.DIDCommMessageMiddleware{},
   346  		})
   347  		require.NoError(t, err)
   348  
   349  		o.connections = &mockConnectionLookup{
   350  			getConnectionByDIDsVal: "mock1",
   351  			getConnectionRecordVal: &connection.Record{},
   352  		}
   353  
   354  		// did rotation err is logged, not returned
   355  		require.NoError(t, o.SendToDID(&service.DIDCommMsgMap{
   356  			"invalid": "message",
   357  		}, testDID, ""))
   358  	})
   359  
   360  	t.Run("resolve err", func(t *testing.T) {
   361  		o, err := NewOutbound(&mockProvider{
   362  			packagerValue: &mockpackager.Packager{},
   363  			vdr: &mockvdr.MockVDRegistry{
   364  				ResolveErr: fmt.Errorf("resolve error"),
   365  			},
   366  			outboundTransportsValue: []transport.OutboundTransport{
   367  				&mockdidcomm.MockOutboundTransport{AcceptValue: true},
   368  			},
   369  			storageProvider:      mockstore.NewMockStoreProvider(),
   370  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   371  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   372  		})
   373  		require.NoError(t, err)
   374  
   375  		o.connections = &mockConnectionLookup{
   376  			getConnectionRecordVal: &connection.Record{},
   377  		}
   378  
   379  		err = o.SendToDID(service.DIDCommMsgMap{}, testDID, "")
   380  		require.Error(t, err)
   381  		require.Contains(t, err.Error(), "resolve error")
   382  	})
   383  
   384  	t.Run("resolve err 2", func(t *testing.T) {
   385  		o, err := NewOutbound(&mockProvider{
   386  			packagerValue: &mockpackager.Packager{},
   387  			vdr: &mockvdr.MockVDRegistry{
   388  				ResolveFunc: countDownMockResolveFunc(mockDoc, 1, fmt.Errorf("resolve error")),
   389  			},
   390  			outboundTransportsValue: []transport.OutboundTransport{
   391  				&mockdidcomm.MockOutboundTransport{AcceptValue: true},
   392  			},
   393  			storageProvider:      mockstore.NewMockStoreProvider(),
   394  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   395  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   396  		})
   397  		require.NoError(t, err)
   398  
   399  		o.connections = &mockConnectionLookup{
   400  			getConnectionRecordVal: &connection.Record{},
   401  		}
   402  
   403  		err = o.SendToDID(service.DIDCommMsgMap{}, testDID, "")
   404  		require.Error(t, err)
   405  		require.Contains(t, err.Error(), "resolve error")
   406  	})
   407  
   408  	t.Run("error if cannot fetch connection record", func(t *testing.T) {
   409  		o, err := NewOutbound(&mockProvider{
   410  			packagerValue: &mockpackager.Packager{},
   411  			vdr: &mockvdr.MockVDRegistry{
   412  				ResolveValue: mockDoc,
   413  			},
   414  			outboundTransportsValue: []transport.OutboundTransport{
   415  				&mockdidcomm.MockOutboundTransport{AcceptValue: true},
   416  			},
   417  			storageProvider:      mockstore.NewMockStoreProvider(),
   418  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   419  			mediaTypeProfiles:    []string{transport.MediaTypeV1EncryptedEnvelope},
   420  		})
   421  		require.NoError(t, err)
   422  
   423  		expected := errors.New("test")
   424  
   425  		o.connections = &mockConnectionLookup{
   426  			getConnectionByDIDsVal: "mock1",
   427  			getConnectionRecordErr: expected,
   428  		}
   429  
   430  		err = o.SendToDID(service.DIDCommMsgMap{}, testDID, "")
   431  		require.ErrorIs(t, err, expected)
   432  		require.Contains(t, err.Error(), "failed to fetch connection record")
   433  	})
   434  
   435  	t.Run("create destination err", func(t *testing.T) {
   436  		o, err := NewOutbound(&mockProvider{
   437  			packagerValue: &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   438  			vdr: &mockvdr.MockVDRegistry{
   439  				ResolveValue: &did.Doc{},
   440  			},
   441  			outboundTransportsValue: []transport.OutboundTransport{
   442  				&mockdidcomm.MockOutboundTransport{AcceptValue: true},
   443  			},
   444  			storageProvider:      mockstore.NewMockStoreProvider(),
   445  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   446  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   447  		})
   448  		require.NoError(t, err)
   449  
   450  		o.connections = &mockConnectionLookup{
   451  			getConnectionByDIDsVal: "mock1",
   452  			getConnectionRecordVal: &connection.Record{},
   453  		}
   454  
   455  		err = o.SendToDID(service.DIDCommMsgMap{}, testDID, "def")
   456  		require.Error(t, err)
   457  		require.Contains(t, err.Error(), "failed to get didcomm destination")
   458  	})
   459  
   460  	t.Run("create destination err 2", func(t *testing.T) {
   461  		o, err := NewOutbound(&mockProvider{
   462  			packagerValue: &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   463  			vdr: &mockvdr.MockVDRegistry{
   464  				ResolveFunc: countDownMockResolveFunc(&did.Doc{}, 1, mockDoc),
   465  			},
   466  			outboundTransportsValue: []transport.OutboundTransport{
   467  				&mockdidcomm.MockOutboundTransport{AcceptValue: true},
   468  			},
   469  			storageProvider:      mockstore.NewMockStoreProvider(),
   470  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   471  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   472  		})
   473  		require.NoError(t, err)
   474  
   475  		o.connections = &mockConnectionLookup{
   476  			getConnectionByDIDsVal: "mock1",
   477  			getConnectionRecordVal: &connection.Record{},
   478  		}
   479  
   480  		err = o.SendToDID(&mockMessage{Type: "foo"}, testDID, "def")
   481  		require.Error(t, err)
   482  		require.Contains(t, err.Error(), "failed to get didcomm destination")
   483  	})
   484  
   485  	t.Run("success event with nil connection record, using default media type profile", func(t *testing.T) {
   486  		o, err := NewOutbound(&mockProvider{
   487  			packagerValue: &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   488  			vdr: &mockvdr.MockVDRegistry{
   489  				ResolveValue: mockDoc,
   490  			},
   491  			outboundTransportsValue: []transport.OutboundTransport{
   492  				&mockdidcomm.MockOutboundTransport{AcceptValue: true},
   493  			},
   494  			storageProvider:      mockstore.NewMockStoreProvider(),
   495  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   496  			mediaTypeProfiles:    []string{transport.MediaTypeV1EncryptedEnvelope},
   497  		})
   498  		require.NoError(t, err)
   499  
   500  		expected := storage.ErrDataNotFound
   501  
   502  		o.connections = &mockConnectionLookup{
   503  			getConnectionRecordErr: expected,
   504  		}
   505  
   506  		require.NoError(t, o.SendToDID(service.DIDCommMsgMap{}, testDID, ""))
   507  	})
   508  
   509  	t.Run("fail with nil connection record, unable to save new record", func(t *testing.T) {
   510  		o, err := NewOutbound(&mockProvider{
   511  			packagerValue: &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   512  			vdr: &mockvdr.MockVDRegistry{
   513  				ResolveValue: mockDoc,
   514  			},
   515  			outboundTransportsValue: []transport.OutboundTransport{
   516  				&mockdidcomm.MockOutboundTransport{AcceptValue: true},
   517  			},
   518  			storageProvider:      mockstore.NewMockStoreProvider(),
   519  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   520  			mediaTypeProfiles:    []string{transport.MediaTypeV1EncryptedEnvelope},
   521  		})
   522  		require.NoError(t, err)
   523  
   524  		expected := fmt.Errorf("store error")
   525  
   526  		o.connections = &mockConnectionLookup{
   527  			getConnectionRecordErr: storage.ErrDataNotFound,
   528  			saveConnectionErr:      expected,
   529  		}
   530  
   531  		err = o.SendToDID(service.DIDCommMsgMap{}, testDID, "")
   532  		require.ErrorIs(t, err, expected)
   533  		require.Contains(t, err.Error(), "failed to save new connection")
   534  	})
   535  
   536  	t.Run("success event with nil connection record, using default media type profile with "+
   537  		"priority", func(t *testing.T) {
   538  		o, err := NewOutbound(&mockProvider{
   539  			packagerValue: &mockpackager.Packager{PackValue: createPackedMsgForForward(t)},
   540  			vdr: &mockvdr.MockVDRegistry{
   541  				ResolveValue: mockDoc,
   542  			},
   543  			outboundTransportsValue: []transport.OutboundTransport{
   544  				&mockdidcomm.MockOutboundTransport{AcceptValue: true},
   545  			},
   546  			storageProvider:      mockstore.NewMockStoreProvider(),
   547  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   548  			mediaTypeProfiles: []string{
   549  				transport.MediaTypeRFC0019EncryptedEnvelope,
   550  				transport.MediaTypeV1EncryptedEnvelope,
   551  				transport.MediaTypeV2EncryptedEnvelope,
   552  			},
   553  		})
   554  		require.NoError(t, err)
   555  
   556  		expected := storage.ErrDataNotFound
   557  
   558  		o.connections = &mockConnectionLookup{
   559  			getConnectionByDIDsVal: "mock1",
   560  			getConnectionRecordErr: expected,
   561  		}
   562  
   563  		require.NoError(t, o.SendToDID(service.DIDCommMsgMap{}, testDID, ""))
   564  	})
   565  }
   566  
   567  func TestOutboundDispatcherTransportReturnRoute(t *testing.T) {
   568  	t.Run("transport route option - value set all", func(t *testing.T) {
   569  		transportReturnRoute := "all"
   570  		req := &decorator.Thread{
   571  			ID: uuid.New().String(),
   572  		}
   573  
   574  		outboundReq := struct {
   575  			*decorator.Transport
   576  			*decorator.Thread
   577  		}{
   578  			&decorator.Transport{ReturnRoute: &decorator.ReturnRoute{Value: transportReturnRoute}},
   579  			req,
   580  		}
   581  		expectedRequest, err := json.Marshal(outboundReq)
   582  		require.NoError(t, err)
   583  		require.NotNil(t, expectedRequest)
   584  
   585  		o, err := NewOutbound(&mockProvider{
   586  			packagerValue: &mockPackager{},
   587  			outboundTransportsValue: []transport.OutboundTransport{
   588  				&mockOutboundTransport{
   589  					expectedRequest: string(expectedRequest),
   590  				},
   591  			},
   592  			transportReturnRoute: transportReturnRoute,
   593  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   594  			storageProvider:      mockstore.NewMockStoreProvider(),
   595  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   596  		})
   597  		require.NoError(t, err)
   598  
   599  		require.NoError(t, o.Send(req, mockdiddoc.MockDIDKey(t),
   600  			&service.Destination{ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{URI: "url"}})}))
   601  	})
   602  
   603  	t.Run("transport route option - value set thread", func(t *testing.T) {
   604  		transportReturnRoute := "thread"
   605  		req := &decorator.Thread{
   606  			ID: uuid.New().String(),
   607  		}
   608  
   609  		outboundReq := struct {
   610  			*decorator.Transport
   611  			*decorator.Thread
   612  		}{
   613  			&decorator.Transport{ReturnRoute: &decorator.ReturnRoute{Value: transportReturnRoute}},
   614  			req,
   615  		}
   616  		expectedRequest, err := json.Marshal(outboundReq)
   617  		require.NoError(t, err)
   618  		require.NotNil(t, expectedRequest)
   619  
   620  		o, err := NewOutbound(&mockProvider{
   621  			packagerValue: &mockPackager{},
   622  			outboundTransportsValue: []transport.OutboundTransport{
   623  				&mockOutboundTransport{
   624  					expectedRequest: string(expectedRequest),
   625  				},
   626  			},
   627  			transportReturnRoute: transportReturnRoute,
   628  			storageProvider:      mockstore.NewMockStoreProvider(),
   629  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   630  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   631  		})
   632  		require.NoError(t, err)
   633  
   634  		require.NoError(t, o.Send(req, mockdiddoc.MockDIDKey(t),
   635  			&service.Destination{ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{URI: "url"}})}))
   636  	})
   637  
   638  	t.Run("transport route option - no value set", func(t *testing.T) {
   639  		req := &decorator.Thread{
   640  			ID: uuid.New().String(),
   641  		}
   642  
   643  		expectedRequest, err := json.Marshal(req)
   644  		require.NoError(t, err)
   645  		require.NotNil(t, expectedRequest)
   646  
   647  		o, err := NewOutbound(&mockProvider{
   648  			packagerValue: &mockPackager{},
   649  			outboundTransportsValue: []transport.OutboundTransport{
   650  				&mockOutboundTransport{
   651  					expectedRequest: string(expectedRequest),
   652  				},
   653  			},
   654  			transportReturnRoute: "",
   655  			storageProvider:      mockstore.NewMockStoreProvider(),
   656  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   657  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   658  		})
   659  		require.NoError(t, err)
   660  
   661  		require.NoError(t, o.Send(req, mockdiddoc.MockDIDKey(t),
   662  			&service.Destination{ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{URI: "url"}})}))
   663  	})
   664  
   665  	t.Run("transport route option - forward message", func(t *testing.T) {
   666  		transportReturnRoute := "thread"
   667  		o, err := NewOutbound(&mockProvider{
   668  			packagerValue:        &mockPackager{},
   669  			transportReturnRoute: transportReturnRoute,
   670  			storageProvider:      mockstore.NewMockStoreProvider(),
   671  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   672  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   673  		})
   674  		require.NoError(t, err)
   675  
   676  		testData := []byte("testData")
   677  
   678  		data, err := o.addTransportRouteOptions(testData,
   679  			&service.Destination{ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{
   680  				{RoutingKeys: []string{"abc"}},
   681  			})})
   682  		require.NoError(t, err)
   683  		require.Equal(t, testData, data)
   684  	})
   685  }
   686  
   687  func TestOutboundDispatcher_Forward(t *testing.T) {
   688  	t.Run("test forward - success", func(t *testing.T) {
   689  		o, err := NewOutbound(&mockProvider{
   690  			packagerValue: &mockpackager.Packager{},
   691  			outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{
   692  				AcceptValue: true,
   693  			}},
   694  			storageProvider:      mockstore.NewMockStoreProvider(),
   695  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   696  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   697  		})
   698  		require.NoError(t, err)
   699  		require.NoError(t, o.Forward("data", &service.Destination{
   700  			ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{URI: "url"}}),
   701  		}))
   702  	})
   703  
   704  	t.Run("test forward - no outbound transport found", func(t *testing.T) {
   705  		o, err := NewOutbound(&mockProvider{
   706  			packagerValue:           &mockpackager.Packager{},
   707  			outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: false}},
   708  			storageProvider:         mockstore.NewMockStoreProvider(),
   709  			protoStorageProvider:    mockstore.NewMockStoreProvider(),
   710  			mediaTypeProfiles:       []string{transport.MediaTypeDIDCommV2Profile},
   711  		})
   712  		require.NoError(t, err)
   713  		err = o.Forward("data", &service.Destination{
   714  			ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{{URI: "url"}}),
   715  		})
   716  		require.Error(t, err)
   717  		require.Contains(t, err.Error(), "outboundDispatcher.Forward: no transport found for serviceEndpoint: url")
   718  	})
   719  
   720  	t.Run("test forward - outbound send failure", func(t *testing.T) {
   721  		o, err := NewOutbound(&mockProvider{
   722  			packagerValue: &mockpackager.Packager{},
   723  			outboundTransportsValue: []transport.OutboundTransport{
   724  				&mockdidcomm.MockOutboundTransport{AcceptValue: true, SendErr: fmt.Errorf("send error")},
   725  			},
   726  			storageProvider:      mockstore.NewMockStoreProvider(),
   727  			protoStorageProvider: mockstore.NewMockStoreProvider(),
   728  			mediaTypeProfiles:    []string{transport.MediaTypeDIDCommV2Profile},
   729  		})
   730  		require.NoError(t, err)
   731  		err = o.Forward("data", &service.Destination{ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.
   732  			DIDCommV2Endpoint{{URI: "url"}})})
   733  		require.Error(t, err)
   734  		require.Contains(t, err.Error(), "send error")
   735  	})
   736  }
   737  
   738  func createPackedMsgForForward(_ *testing.T) []byte {
   739  	return []byte("")
   740  }
   741  
   742  // mockProvider mock provider.
   743  type mockProvider struct {
   744  	packagerValue           transport.Packager
   745  	outboundTransportsValue []transport.OutboundTransport
   746  	transportReturnRoute    string
   747  	vdr                     vdrapi.Registry
   748  	kms                     kms.KeyManager
   749  	storageProvider         storage.Provider
   750  	protoStorageProvider    storage.Provider
   751  	mediaTypeProfiles       []string
   752  	keyAgreementType        kms.KeyType
   753  	didRotator              middleware.DIDCommMessageMiddleware
   754  }
   755  
   756  func (p *mockProvider) Packager() transport.Packager {
   757  	return p.packagerValue
   758  }
   759  
   760  func (p *mockProvider) OutboundTransports() []transport.OutboundTransport {
   761  	return p.outboundTransportsValue
   762  }
   763  
   764  func (p *mockProvider) TransportReturnRoute() string {
   765  	return p.transportReturnRoute
   766  }
   767  
   768  func (p *mockProvider) VDRegistry() vdrapi.Registry {
   769  	return p.vdr
   770  }
   771  
   772  func (p *mockProvider) KMS() kms.KeyManager {
   773  	if p.kms != nil {
   774  		return p.kms
   775  	}
   776  
   777  	return &mockkms.KeyManager{}
   778  }
   779  
   780  func (p *mockProvider) StorageProvider() storage.Provider {
   781  	return p.storageProvider
   782  }
   783  
   784  func (p *mockProvider) ProtocolStateStorageProvider() storage.Provider {
   785  	return p.protoStorageProvider
   786  }
   787  
   788  func (p *mockProvider) MediaTypeProfiles() []string {
   789  	return p.mediaTypeProfiles
   790  }
   791  
   792  func (p *mockProvider) KeyAgreementType() kms.KeyType {
   793  	return p.keyAgreementType
   794  }
   795  
   796  func (p *mockProvider) DIDRotator() *middleware.DIDCommMessageMiddleware {
   797  	return &p.didRotator
   798  }
   799  
   800  // mockOutboundTransport mock outbound transport.
   801  type mockOutboundTransport struct {
   802  	expectedRequest string
   803  	acceptRecipient bool
   804  }
   805  
   806  func (o *mockOutboundTransport) Start(prov transport.Provider) error {
   807  	return nil
   808  }
   809  
   810  func (o *mockOutboundTransport) Send(data []byte, destination *service.Destination) (string, error) {
   811  	if string(data) != o.expectedRequest {
   812  		return "", errors.New("invalid request")
   813  	}
   814  
   815  	return "", nil
   816  }
   817  
   818  func (o *mockOutboundTransport) AcceptRecipient([]string) bool {
   819  	return o.acceptRecipient
   820  }
   821  
   822  func (o *mockOutboundTransport) Accept(url string) bool {
   823  	return true
   824  }
   825  
   826  // mockPackager mock packager.
   827  type mockPackager struct {
   828  	mock.Mock
   829  }
   830  
   831  func (m *mockPackager) PackMessage(e *transport.Envelope) ([]byte, error) {
   832  	if len(m.ExpectedCalls) > 0 {
   833  		args := m.Called(e.ToKeys)
   834  		switch v := args.Get(0).(type) {
   835  		case []byte:
   836  			return v, nil
   837  		default:
   838  			return e.Message, nil
   839  		}
   840  	}
   841  
   842  	return e.Message, nil
   843  }
   844  
   845  func (m *mockPackager) UnpackMessage(encMessage []byte) (*transport.Envelope, error) {
   846  	return nil, nil
   847  }
   848  
   849  type mockConnectionLookup struct {
   850  	getConnectionByDIDsVal string
   851  	getConnectionByDIDsErr error
   852  	getConnectionRecordVal *connection.Record
   853  	getConnectionRecordErr error
   854  	saveConnectionErr      error
   855  }
   856  
   857  func (m *mockConnectionLookup) GetConnectionIDByDIDs(myDID, theirDID string) (string, error) {
   858  	return m.getConnectionByDIDsVal, m.getConnectionByDIDsErr
   859  }
   860  
   861  func (m *mockConnectionLookup) GetConnectionRecord(s string) (*connection.Record, error) {
   862  	return m.getConnectionRecordVal, m.getConnectionRecordErr
   863  }
   864  
   865  func (m *mockConnectionLookup) GetConnectionRecordByDIDs(myDID, theirDID string) (*connection.Record, error) {
   866  	if m.getConnectionByDIDsErr != nil {
   867  		return nil, m.getConnectionByDIDsErr
   868  	}
   869  
   870  	return m.getConnectionRecordVal, m.getConnectionRecordErr
   871  }
   872  
   873  func (m *mockConnectionLookup) SaveConnectionRecord(record *connection.Record) error {
   874  	return m.saveConnectionErr
   875  }
   876  
   877  func countDownMockResolveFunc(first interface{}, countFirst int, rest interface{},
   878  ) func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
   879  	var (
   880  		firstDoc *did.Doc
   881  		restDoc  *did.Doc
   882  		firstErr error
   883  		restErr  error
   884  	)
   885  
   886  	switch f := first.(type) {
   887  	case *did.Doc:
   888  		firstDoc = f
   889  	case error:
   890  		firstErr = f
   891  	}
   892  
   893  	switch r := rest.(type) {
   894  	case *did.Doc:
   895  		restDoc = r
   896  	case error:
   897  		restErr = r
   898  	}
   899  
   900  	return func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
   901  		if countFirst <= 0 {
   902  			return &did.DocResolution{DIDDocument: restDoc}, restErr
   903  		}
   904  
   905  		countFirst--
   906  
   907  		return &did.DocResolution{DIDDocument: firstDoc}, firstErr
   908  	}
   909  }