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 }