github.com/hyperledger/aries-framework-go@v0.3.2/pkg/wallet/didcomm_test.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package wallet
     8  
     9  import (
    10  	_ "embed"
    11  	"encoding/json"
    12  	"errors"
    13  	"fmt"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/golang/mock/gomock"
    18  	"github.com/google/uuid"
    19  	"github.com/stretchr/testify/require"
    20  
    21  	"github.com/hyperledger/aries-framework-go/pkg/client/outofband"
    22  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model"
    23  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
    24  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/didexchange"
    25  	issuecredentialsvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/issuecredential"
    26  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator"
    27  	outofbandSvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/outofband"
    28  	oobv2 "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/outofbandv2"
    29  	presentproofSvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/presentproof"
    30  	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
    31  	mockoutofbandv2 "github.com/hyperledger/aries-framework-go/pkg/internal/gomocks/client/outofbandv2"
    32  	"github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil"
    33  	mockdidexchange "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/didexchange"
    34  	mockissuecredential "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/issuecredential"
    35  	mockmediator "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/mediator"
    36  	mockoutofband "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/outofband"
    37  	mockpresentproof "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/presentproof"
    38  	mockprovider "github.com/hyperledger/aries-framework-go/pkg/mock/provider"
    39  	mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage"
    40  	"github.com/hyperledger/aries-framework-go/pkg/store/connection"
    41  )
    42  
    43  const (
    44  	exampleWebRedirect = "http://example.com/sample"
    45  	sampleMsgComment   = "sample mock msg"
    46  )
    47  
    48  func TestNewDidComm(t *testing.T) {
    49  	t.Run("test get wallet failure - present proof client initialize error", func(t *testing.T) {
    50  		mockctx := newDidCommMockProvider(t)
    51  		delete(mockctx.ServiceMap, presentproofSvc.Name)
    52  
    53  		err := CreateProfile(sampleUserID, mockctx, WithPassphrase(samplePassPhrase))
    54  		require.NoError(t, err)
    55  
    56  		wallet, err := New(sampleUserID, mockctx)
    57  		require.NoError(t, err)
    58  		require.NotEmpty(t, wallet)
    59  
    60  		didcomm, err := NewDidComm(wallet, mockctx)
    61  		require.Error(t, err)
    62  		require.Empty(t, didcomm)
    63  
    64  		require.Contains(t, err.Error(), "failed to initialize present proof client")
    65  	})
    66  
    67  	t.Run("test get wallet failure - oob client initialize error", func(t *testing.T) {
    68  		mockctx := newDidCommMockProvider(t)
    69  		delete(mockctx.ServiceMap, outofbandSvc.Name)
    70  
    71  		err := CreateProfile(sampleUserID, mockctx, WithPassphrase(samplePassPhrase))
    72  		require.NoError(t, err)
    73  
    74  		wallet, err := New(sampleUserID, mockctx)
    75  		require.NoError(t, err)
    76  		require.NotEmpty(t, wallet)
    77  
    78  		didcomm, err := NewDidComm(wallet, mockctx)
    79  		require.Error(t, err)
    80  		require.Empty(t, didcomm)
    81  		require.Contains(t, err.Error(), "failed to initialize out-of-band client")
    82  	})
    83  
    84  	t.Run("test get wallet failure - oob client initialize error", func(t *testing.T) {
    85  		mockctx := newDidCommMockProvider(t)
    86  		delete(mockctx.ServiceMap, didexchange.DIDExchange)
    87  
    88  		err := CreateProfile(sampleUserID, mockctx, WithPassphrase(samplePassPhrase))
    89  		require.NoError(t, err)
    90  
    91  		wallet, err := New(sampleUserID, mockctx)
    92  		require.NoError(t, err)
    93  		require.NotEmpty(t, wallet)
    94  
    95  		didcomm, err := NewDidComm(wallet, mockctx)
    96  		require.Error(t, err)
    97  		require.Empty(t, didcomm)
    98  		require.Contains(t, err.Error(), "failed to initialize didexchange client")
    99  	})
   100  
   101  	t.Run("test get wallet failure - connection lookup initialize error", func(t *testing.T) {
   102  		mockctx := newDidCommMockProvider(t)
   103  		mockStoreProvider := mockstorage.NewMockStoreProvider()
   104  		mockStoreProvider.FailNamespace = "didexchange"
   105  		mockctx.StorageProviderValue = mockStoreProvider
   106  
   107  		err := CreateProfile(sampleUserID, mockctx, WithPassphrase(samplePassPhrase))
   108  		require.NoError(t, err)
   109  
   110  		wallet, err := New(sampleUserID, mockctx)
   111  		require.NoError(t, err)
   112  		require.NotEmpty(t, wallet)
   113  
   114  		didcomm, err := NewDidComm(wallet, mockctx)
   115  		require.Error(t, err)
   116  		require.Empty(t, didcomm)
   117  		require.Contains(t, err.Error(), "failed to initialize connection lookup")
   118  	})
   119  }
   120  
   121  func TestWallet_Connect(t *testing.T) {
   122  	sampleDIDCommUser := uuid.New().String()
   123  	mockctx := newDidCommMockProvider(t)
   124  	err := CreateProfile(sampleDIDCommUser, mockctx, WithPassphrase(samplePassPhrase))
   125  	require.NoError(t, err)
   126  
   127  	t.Run("test did connect success", func(t *testing.T) {
   128  		sampleConnID := uuid.New().String()
   129  
   130  		oobSvc := &mockoutofband.MockOobService{
   131  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
   132  				return sampleConnID, nil
   133  			},
   134  		}
   135  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
   136  
   137  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
   138  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   139  				ch <- service.StateMsg{
   140  					Type:       service.PostState,
   141  					StateID:    didexchange.StateIDCompleted,
   142  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
   143  				}
   144  
   145  				return nil
   146  			},
   147  		}
   148  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
   149  
   150  		wallet, err := New(sampleDIDCommUser, mockctx)
   151  		require.NoError(t, err)
   152  		require.NotEmpty(t, wallet)
   153  
   154  		didcomm, err := NewDidComm(wallet, mockctx)
   155  		require.NoError(t, err)
   156  		require.NotEmpty(t, didcomm)
   157  
   158  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   159  		require.NoError(t, err)
   160  		require.NotEmpty(t, token)
   161  
   162  		defer wallet.Close()
   163  
   164  		connectionID, err := didcomm.Connect(token, &outofband.Invitation{})
   165  		require.NoError(t, err)
   166  		require.Equal(t, sampleConnID, connectionID)
   167  	})
   168  
   169  	t.Run("test did connect failure - accept invitation failure", func(t *testing.T) {
   170  		oobSvc := &mockoutofband.MockOobService{
   171  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
   172  				return "", fmt.Errorf(sampleWalletErr)
   173  			},
   174  		}
   175  
   176  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
   177  
   178  		wallet, err := New(sampleDIDCommUser, mockctx)
   179  		require.NoError(t, err)
   180  		require.NotEmpty(t, wallet)
   181  
   182  		didcomm, err := NewDidComm(wallet, mockctx)
   183  		require.NoError(t, err)
   184  		require.NotEmpty(t, didcomm)
   185  
   186  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   187  		require.NoError(t, err)
   188  		require.NotEmpty(t, token)
   189  
   190  		defer wallet.Close()
   191  
   192  		connectionID, err := didcomm.Connect(token, &outofband.Invitation{}, WithConnectTimeout(1*time.Millisecond))
   193  		require.Error(t, err)
   194  		require.Contains(t, err.Error(), sampleWalletErr)
   195  		require.Contains(t, err.Error(), "failed to accept invitation")
   196  		require.Empty(t, connectionID)
   197  	})
   198  
   199  	t.Run("test did connect failure - register event failure", func(t *testing.T) {
   200  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
   201  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   202  				return fmt.Errorf(sampleWalletErr)
   203  			},
   204  		}
   205  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
   206  
   207  		wallet, err := New(sampleDIDCommUser, mockctx)
   208  		require.NoError(t, err)
   209  		require.NotEmpty(t, wallet)
   210  
   211  		didcomm, err := NewDidComm(wallet, mockctx)
   212  		require.NoError(t, err)
   213  		require.NotEmpty(t, didcomm)
   214  
   215  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   216  		require.NoError(t, err)
   217  		require.NotEmpty(t, token)
   218  
   219  		defer wallet.Close()
   220  
   221  		connectionID, err := didcomm.Connect(token, &outofband.Invitation{}, WithConnectTimeout(1*time.Millisecond))
   222  		require.Error(t, err)
   223  		require.Contains(t, err.Error(), sampleWalletErr)
   224  		require.Contains(t, err.Error(), "failed to register msg event")
   225  		require.Empty(t, connectionID)
   226  	})
   227  
   228  	t.Run("test did connect failure - state not completed", func(t *testing.T) {
   229  		mockctx.ServiceMap[outofbandSvc.Name] = &mockoutofband.MockOobService{}
   230  		mockctx.ServiceMap[didexchange.DIDExchange] = &mockdidexchange.MockDIDExchangeSvc{}
   231  
   232  		wallet, err := New(sampleDIDCommUser, mockctx)
   233  		require.NoError(t, err)
   234  		require.NotEmpty(t, wallet)
   235  
   236  		didcomm, err := NewDidComm(wallet, mockctx)
   237  		require.NoError(t, err)
   238  		require.NotEmpty(t, didcomm)
   239  
   240  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   241  		require.NoError(t, err)
   242  		require.NotEmpty(t, token)
   243  
   244  		defer wallet.Close()
   245  
   246  		connectionID, err := didcomm.Connect(token, &outofband.Invitation{}, WithConnectTimeout(1*time.Millisecond))
   247  		require.Error(t, err)
   248  		require.Contains(t, err.Error(), "time out waiting for did exchange state 'completed'")
   249  		require.Empty(t, connectionID)
   250  	})
   251  
   252  	t.Run("test did connect success - with warnings", func(t *testing.T) {
   253  		sampleConnID := uuid.New().String()
   254  
   255  		oobSvc := &mockoutofband.MockOobService{
   256  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
   257  				return sampleConnID, nil
   258  			},
   259  		}
   260  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
   261  
   262  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
   263  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   264  				ch <- service.StateMsg{
   265  					Type:    service.PreState,
   266  					StateID: didexchange.StateIDCompleted,
   267  				}
   268  
   269  				ch <- service.StateMsg{
   270  					Type:    service.PostState,
   271  					StateID: didexchange.StateIDCompleted,
   272  				}
   273  
   274  				ch <- service.StateMsg{
   275  					Type:       service.PostState,
   276  					StateID:    didexchange.StateIDCompleted,
   277  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
   278  				}
   279  
   280  				return nil
   281  			},
   282  			UnregisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   283  				return fmt.Errorf(sampleWalletErr)
   284  			},
   285  		}
   286  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
   287  
   288  		wallet, err := New(sampleDIDCommUser, mockctx)
   289  		require.NoError(t, err)
   290  		require.NotEmpty(t, wallet)
   291  
   292  		didcomm, err := NewDidComm(wallet, mockctx)
   293  		require.NoError(t, err)
   294  		require.NotEmpty(t, didcomm)
   295  
   296  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   297  		require.NoError(t, err)
   298  		require.NotEmpty(t, token)
   299  
   300  		defer wallet.Close()
   301  
   302  		connectionID, err := didcomm.Connect(token, &outofband.Invitation{})
   303  		require.NoError(t, err)
   304  		require.Equal(t, sampleConnID, connectionID)
   305  	})
   306  
   307  	t.Run("test oob connect options", func(t *testing.T) {
   308  		options := []ConnectOptions{
   309  			WithConnectTimeout(10 * time.Second),
   310  			WithRouterConnections("sample-conn"),
   311  			WithMyLabel("sample-label"),
   312  			WithReuseAnyConnection(true),
   313  			WithReuseDID("sample-did"),
   314  		}
   315  
   316  		opts := &connectOpts{}
   317  		for _, opt := range options {
   318  			opt(opts)
   319  		}
   320  
   321  		require.Equal(t, opts.timeout, 10*time.Second)
   322  		require.Equal(t, opts.Connections[0], "sample-conn")
   323  		require.Equal(t, opts.MyLabel(), "sample-label")
   324  		require.Equal(t, opts.ReuseDID, "sample-did")
   325  		require.True(t, opts.ReuseAny)
   326  
   327  		require.Len(t, getOobMessageOptions(opts), 3)
   328  	})
   329  }
   330  
   331  func TestWallet_ProposePresentation(t *testing.T) {
   332  	sampleDIDCommUser := uuid.New().String()
   333  	mockctx := newDidCommMockProvider(t)
   334  	err := CreateProfile(sampleDIDCommUser, mockctx, WithPassphrase(samplePassPhrase))
   335  	require.NoError(t, err)
   336  
   337  	const (
   338  		myDID    = "did:mydid:123"
   339  		theirDID = "did:theirdid:123"
   340  	)
   341  
   342  	t.Run("test propose presentation success - didcomm v1", func(t *testing.T) {
   343  		sampleConnID := uuid.New().String()
   344  
   345  		oobSvc := &mockoutofband.MockOobService{
   346  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
   347  				return sampleConnID, nil
   348  			},
   349  		}
   350  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
   351  
   352  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
   353  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   354  				ch <- service.StateMsg{
   355  					Type:       service.PostState,
   356  					StateID:    didexchange.StateIDCompleted,
   357  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
   358  				}
   359  
   360  				return nil
   361  			},
   362  		}
   363  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
   364  
   365  		thID := uuid.New().String()
   366  
   367  		ppSvc := &mockpresentproof.MockPresentProofSvc{
   368  			ActionsFunc: func() ([]presentproofSvc.Action, error) {
   369  				return []presentproofSvc.Action{
   370  					{
   371  						PIID: thID,
   372  						Msg: service.NewDIDCommMsgMap(&presentproofSvc.RequestPresentationV2{
   373  							Comment: "mock msg",
   374  						}),
   375  						MyDID:    myDID,
   376  						TheirDID: theirDID,
   377  					},
   378  				}, nil
   379  			},
   380  			HandleFunc: func(service.DIDCommMsg) (string, error) {
   381  				return thID, nil
   382  			},
   383  		}
   384  
   385  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
   386  		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
   387  
   388  		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
   389  		require.NoError(t, err)
   390  
   391  		record := &connection.Record{
   392  			ConnectionID: sampleConnID,
   393  			MyDID:        myDID,
   394  			TheirDID:     theirDID,
   395  		}
   396  		recordBytes, err := json.Marshal(record)
   397  		require.NoError(t, err)
   398  		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
   399  
   400  		wallet, err := New(sampleDIDCommUser, mockctx)
   401  		require.NoError(t, err)
   402  		require.NotEmpty(t, wallet)
   403  
   404  		didcomm, err := NewDidComm(wallet, mockctx)
   405  		require.NoError(t, err)
   406  		require.NotEmpty(t, didcomm)
   407  
   408  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   409  		require.NoError(t, err)
   410  		require.NotEmpty(t, token)
   411  
   412  		defer wallet.Close()
   413  
   414  		invitation := GenericInvitation{}
   415  		err = json.Unmarshal([]byte(`{
   416  			"@id": "abc123",
   417  			"@type": "https://didcomm.org/out-of-band/1.0/invitation"
   418  		}`), &invitation)
   419  		require.NoError(t, err)
   420  
   421  		msg, err := didcomm.ProposePresentation(token, &invitation,
   422  			WithConnectOptions(WithConnectTimeout(1*time.Millisecond)))
   423  		require.NoError(t, err)
   424  		require.NotEmpty(t, msg)
   425  
   426  		// empty invitation defaults to DIDComm v1
   427  		msg, err = didcomm.ProposePresentation(token, &GenericInvitation{},
   428  			WithConnectOptions(WithConnectTimeout(1*time.Millisecond)))
   429  		require.NoError(t, err)
   430  		require.NotEmpty(t, msg)
   431  
   432  		// invitation with unknown version defaults to DIDComm v1
   433  		msg, err = didcomm.ProposePresentation(token, &GenericInvitation{version: "unknown"},
   434  			WithConnectOptions(WithConnectTimeout(1*time.Millisecond)))
   435  		require.NoError(t, err)
   436  		require.NotEmpty(t, msg)
   437  	})
   438  
   439  	t.Run("test propose presentation success - didcomm v2", func(t *testing.T) {
   440  		sampleConnID := uuid.New().String()
   441  
   442  		ctrl := gomock.NewController(t)
   443  		defer ctrl.Finish()
   444  
   445  		oobv2Svc := mockoutofbandv2.NewMockOobService(ctrl)
   446  		oobv2Svc.EXPECT().AcceptInvitation(gomock.Any(), gomock.Any()).Return(sampleConnID, nil).AnyTimes()
   447  
   448  		mockctx.ServiceMap[oobv2.Name] = oobv2Svc
   449  
   450  		thID := uuid.New().String()
   451  
   452  		ppSvc := &mockpresentproof.MockPresentProofSvc{
   453  			ActionsFunc: func() ([]presentproofSvc.Action, error) {
   454  				return []presentproofSvc.Action{
   455  					{
   456  						PIID: thID,
   457  						Msg: service.NewDIDCommMsgMap(&presentproofSvc.RequestPresentationV3{
   458  							Body: presentproofSvc.RequestPresentationV3Body{
   459  								Comment: "mock msg",
   460  							},
   461  						}),
   462  						MyDID:    myDID,
   463  						TheirDID: theirDID,
   464  					},
   465  				}, nil
   466  			},
   467  			HandleFunc: func(service.DIDCommMsg) (string, error) {
   468  				return thID, nil
   469  			},
   470  		}
   471  
   472  		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
   473  
   474  		connRec, err := connection.NewRecorder(mockctx)
   475  		require.NoError(t, err)
   476  
   477  		record := &connection.Record{
   478  			ConnectionID:   sampleConnID,
   479  			MyDID:          myDID,
   480  			TheirDID:       theirDID,
   481  			DIDCommVersion: service.V2,
   482  			State:          connection.StateNameCompleted,
   483  		}
   484  
   485  		err = connRec.SaveConnectionRecord(record)
   486  		require.NoError(t, err)
   487  
   488  		wallet, err := New(sampleDIDCommUser, mockctx)
   489  		require.NoError(t, err)
   490  		require.NotEmpty(t, wallet)
   491  
   492  		didcomm, err := NewDidComm(wallet, mockctx)
   493  		require.NoError(t, err)
   494  		require.NotEmpty(t, didcomm)
   495  
   496  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   497  		require.NoError(t, err)
   498  		require.NotEmpty(t, token)
   499  
   500  		defer wallet.Close()
   501  
   502  		invitation := GenericInvitation{}
   503  		err = json.Unmarshal([]byte(`{
   504  			"id": "abc123",
   505  			"type": "https://didcomm.org/out-of-band/2.0/invitation"
   506  		}`), &invitation)
   507  		require.NoError(t, err)
   508  
   509  		msg, err := didcomm.ProposePresentation(token, &invitation,
   510  			WithConnectOptions(WithConnectTimeout(1*time.Millisecond)))
   511  		require.NoError(t, err)
   512  		require.NotEmpty(t, msg)
   513  	})
   514  
   515  	t.Run("test propose presentation failure - did connect failure", func(t *testing.T) {
   516  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
   517  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   518  				return fmt.Errorf(sampleWalletErr)
   519  			},
   520  		}
   521  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
   522  
   523  		wallet, err := New(sampleDIDCommUser, mockctx)
   524  		require.NoError(t, err)
   525  		require.NotEmpty(t, wallet)
   526  
   527  		didcomm, err := NewDidComm(wallet, mockctx)
   528  		require.NoError(t, err)
   529  		require.NotEmpty(t, didcomm)
   530  
   531  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   532  		require.NoError(t, err)
   533  		require.NotEmpty(t, token)
   534  
   535  		defer wallet.Close()
   536  
   537  		msg, err := didcomm.ProposePresentation(token, &GenericInvitation{})
   538  		require.Error(t, err)
   539  		require.Contains(t, err.Error(), sampleWalletErr)
   540  		require.Contains(t, err.Error(), "failed to perform did connection")
   541  		require.Empty(t, msg)
   542  	})
   543  
   544  	t.Run("test propose presentation failure - no connection found", func(t *testing.T) {
   545  		sampleConnID := uuid.New().String()
   546  
   547  		oobSvc := &mockoutofband.MockOobService{
   548  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
   549  				return sampleConnID, nil
   550  			},
   551  		}
   552  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
   553  
   554  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
   555  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   556  				ch <- service.StateMsg{
   557  					Type:       service.PostState,
   558  					StateID:    didexchange.StateIDCompleted,
   559  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
   560  				}
   561  
   562  				return nil
   563  			},
   564  		}
   565  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
   566  
   567  		wallet, err := New(sampleDIDCommUser, mockctx)
   568  		require.NoError(t, err)
   569  		require.NotEmpty(t, wallet)
   570  
   571  		didcomm, err := NewDidComm(wallet, mockctx)
   572  		require.NoError(t, err)
   573  		require.NotEmpty(t, didcomm)
   574  
   575  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   576  		require.NoError(t, err)
   577  		require.NotEmpty(t, token)
   578  
   579  		defer wallet.Close()
   580  
   581  		msg, err := didcomm.ProposePresentation(token, &GenericInvitation{})
   582  		require.Error(t, err)
   583  		require.Contains(t, err.Error(), "failed to lookup connection")
   584  		require.Empty(t, msg)
   585  	})
   586  
   587  	t.Run("test propose presentation failure - failed to send", func(t *testing.T) {
   588  		sampleConnID := uuid.New().String()
   589  
   590  		oobSvc := &mockoutofband.MockOobService{
   591  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
   592  				return sampleConnID, nil
   593  			},
   594  		}
   595  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
   596  
   597  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
   598  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   599  				ch <- service.StateMsg{
   600  					Type:       service.PostState,
   601  					StateID:    didexchange.StateIDCompleted,
   602  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
   603  				}
   604  
   605  				return nil
   606  			},
   607  		}
   608  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
   609  
   610  		ppSvc := &mockpresentproof.MockPresentProofSvc{
   611  			HandleFunc: func(service.DIDCommMsg) (string, error) {
   612  				return "", fmt.Errorf(sampleWalletErr)
   613  			},
   614  			HandleOutboundFunc: func(service.DIDCommMsg, string, string) (string, error) {
   615  				return "", fmt.Errorf(sampleWalletErr)
   616  			},
   617  		}
   618  		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
   619  
   620  		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
   621  		require.NoError(t, err)
   622  
   623  		record := &connection.Record{
   624  			ConnectionID: sampleConnID,
   625  			MyDID:        myDID,
   626  			TheirDID:     theirDID,
   627  		}
   628  		recordBytes, err := json.Marshal(record)
   629  		require.NoError(t, err)
   630  		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
   631  
   632  		wallet, err := New(sampleDIDCommUser, mockctx)
   633  		require.NoError(t, err)
   634  		require.NotEmpty(t, wallet)
   635  
   636  		didcomm, err := NewDidComm(wallet, mockctx)
   637  		require.NoError(t, err)
   638  		require.NotEmpty(t, didcomm)
   639  
   640  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   641  		require.NoError(t, err)
   642  		require.NotEmpty(t, token)
   643  
   644  		defer wallet.Close()
   645  
   646  		msg, err := didcomm.ProposePresentation(token, &GenericInvitation{})
   647  		require.Error(t, err)
   648  		require.Contains(t, err.Error(), sampleWalletErr)
   649  		require.Contains(t, err.Error(), "failed to propose presentation from wallet")
   650  		require.Empty(t, msg)
   651  	})
   652  
   653  	t.Run("test propose presentation failure - timeout waiting for presentation request", func(t *testing.T) {
   654  		sampleConnID := uuid.New().String()
   655  
   656  		oobSvc := &mockoutofband.MockOobService{
   657  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
   658  				return sampleConnID, nil
   659  			},
   660  		}
   661  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
   662  
   663  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
   664  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   665  				ch <- service.StateMsg{
   666  					Type:       service.PostState,
   667  					StateID:    didexchange.StateIDCompleted,
   668  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
   669  				}
   670  
   671  				return nil
   672  			},
   673  		}
   674  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
   675  
   676  		ppSvc := &mockpresentproof.MockPresentProofSvc{
   677  			HandleFunc: func(service.DIDCommMsg) (string, error) {
   678  				return uuid.New().String(), nil
   679  			},
   680  		}
   681  		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
   682  
   683  		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
   684  		require.NoError(t, err)
   685  
   686  		record := &connection.Record{
   687  			ConnectionID: sampleConnID,
   688  			MyDID:        myDID,
   689  			TheirDID:     theirDID,
   690  		}
   691  		recordBytes, err := json.Marshal(record)
   692  		require.NoError(t, err)
   693  		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
   694  
   695  		wallet, err := New(sampleDIDCommUser, mockctx)
   696  		require.NoError(t, err)
   697  		require.NotEmpty(t, wallet)
   698  
   699  		didcomm, err := NewDidComm(wallet, mockctx)
   700  		require.NoError(t, err)
   701  		require.NotEmpty(t, didcomm)
   702  
   703  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   704  		require.NoError(t, err)
   705  		require.NotEmpty(t, token)
   706  
   707  		defer wallet.Close()
   708  
   709  		msg, err := didcomm.ProposePresentation(token, &GenericInvitation{}, WithInitiateTimeout(600*time.Millisecond))
   710  		require.Error(t, err)
   711  		require.Contains(t, err.Error(), "timeout waiting for request presentation message")
   712  		require.Empty(t, msg)
   713  	})
   714  
   715  	t.Run("test propose presentation failure - action error", func(t *testing.T) {
   716  		sampleConnID := uuid.New().String()
   717  
   718  		oobSvc := &mockoutofband.MockOobService{
   719  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
   720  				return sampleConnID, nil
   721  			},
   722  		}
   723  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
   724  
   725  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
   726  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   727  				ch <- service.StateMsg{
   728  					Type:       service.PostState,
   729  					StateID:    didexchange.StateIDCompleted,
   730  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
   731  				}
   732  
   733  				return nil
   734  			},
   735  		}
   736  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
   737  
   738  		ppSvc := &mockpresentproof.MockPresentProofSvc{
   739  			HandleFunc: func(service.DIDCommMsg) (string, error) {
   740  				return uuid.New().String(), nil
   741  			},
   742  			ActionsFunc: func() ([]presentproofSvc.Action, error) {
   743  				return nil, fmt.Errorf(sampleWalletErr)
   744  			},
   745  		}
   746  		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
   747  
   748  		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
   749  		require.NoError(t, err)
   750  
   751  		record := &connection.Record{
   752  			ConnectionID: sampleConnID,
   753  			TheirDID:     theirDID,
   754  		}
   755  		recordBytes, err := json.Marshal(record)
   756  		require.NoError(t, err)
   757  		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
   758  
   759  		wallet, err := New(sampleDIDCommUser, mockctx)
   760  		require.NoError(t, err)
   761  		require.NotEmpty(t, wallet)
   762  
   763  		didcomm, err := NewDidComm(wallet, mockctx)
   764  		require.NoError(t, err)
   765  		require.NotEmpty(t, didcomm)
   766  
   767  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   768  		require.NoError(t, err)
   769  		require.NotEmpty(t, token)
   770  
   771  		defer wallet.Close()
   772  
   773  		msg, err := didcomm.ProposePresentation(token, &GenericInvitation{}, WithInitiateTimeout(1*time.Millisecond),
   774  			WithFromDID("did:sample:from"))
   775  		require.Error(t, err)
   776  		require.Contains(t, err.Error(), "timeout waiting for request presentation message")
   777  		require.Empty(t, msg)
   778  	})
   779  
   780  	t.Run("test propose presentation failure - oob v2 accept error", func(t *testing.T) {
   781  		ctrl := gomock.NewController(t)
   782  		defer ctrl.Finish()
   783  
   784  		expectErr := fmt.Errorf("expected error")
   785  
   786  		oobv2Svc := mockoutofbandv2.NewMockOobService(ctrl)
   787  		oobv2Svc.EXPECT().AcceptInvitation(gomock.Any(), gomock.Any()).Return("", expectErr).AnyTimes()
   788  
   789  		mockctx.ServiceMap[oobv2.Name] = oobv2Svc
   790  
   791  		wallet, err := New(sampleDIDCommUser, mockctx)
   792  		require.NoError(t, err)
   793  		require.NotEmpty(t, wallet)
   794  
   795  		didcomm, err := NewDidComm(wallet, mockctx)
   796  		require.NoError(t, err)
   797  		require.NotEmpty(t, didcomm)
   798  
   799  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   800  		require.NoError(t, err)
   801  		require.NotEmpty(t, token)
   802  
   803  		defer wallet.Close()
   804  
   805  		invitation := GenericInvitation{}
   806  		err = json.Unmarshal([]byte(`{
   807  			"id": "abc123",
   808  			"type": "https://didcomm.org/out-of-band/2.0/invitation"
   809  		}`), &invitation)
   810  		require.NoError(t, err)
   811  
   812  		_, err = didcomm.ProposePresentation(token, &invitation,
   813  			WithConnectOptions(WithConnectTimeout(1*time.Millisecond)))
   814  		require.Error(t, err)
   815  		require.ErrorIs(t, err, expectErr)
   816  		require.Contains(t, err.Error(), "failed to accept OOB v2 invitation")
   817  	})
   818  }
   819  
   820  func TestWallet_PresentProof(t *testing.T) {
   821  	sampleDIDCommUser := uuid.New().String()
   822  	mockctx := newDidCommMockProvider(t)
   823  	err := CreateProfile(sampleDIDCommUser, mockctx, WithPassphrase(samplePassPhrase))
   824  	require.NoError(t, err)
   825  
   826  	t.Run("test present proof success", func(t *testing.T) {
   827  		wallet, err := New(sampleDIDCommUser, mockctx)
   828  		require.NoError(t, err)
   829  		require.NotEmpty(t, wallet)
   830  
   831  		didcomm, err := NewDidComm(wallet, mockctx)
   832  		require.NoError(t, err)
   833  		require.NotEmpty(t, didcomm)
   834  
   835  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   836  		require.NoError(t, err)
   837  		require.NotEmpty(t, token)
   838  
   839  		defer wallet.Close()
   840  
   841  		response, err := didcomm.PresentProof(token, uuid.New().String(), FromPresentation(&verifiable.Presentation{}))
   842  		require.NoError(t, err)
   843  		require.NotEmpty(t, response)
   844  		require.Equal(t, model.AckStatusPENDING, response.Status)
   845  	})
   846  
   847  	t.Run("test present proof success - wait for done with redirect", func(t *testing.T) {
   848  		thID := uuid.New().String()
   849  		mockPresentProofSvc := &mockpresentproof.MockPresentProofSvc{
   850  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   851  				ch <- service.StateMsg{
   852  					Type:    service.PostState,
   853  					StateID: presentproofSvc.StateNameDone,
   854  					Properties: &mockdidexchange.MockEventProperties{
   855  						Properties: map[string]interface{}{
   856  							webRedirectStatusKey: model.AckStatusOK,
   857  							webRedirectURLKey:    exampleWebRedirect,
   858  						},
   859  					},
   860  					Msg: &mockMsg{thID: thID},
   861  				}
   862  
   863  				return nil
   864  			},
   865  		}
   866  		mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc
   867  
   868  		wallet, err := New(sampleDIDCommUser, mockctx)
   869  		require.NoError(t, err)
   870  		require.NotEmpty(t, wallet)
   871  
   872  		didcomm, err := NewDidComm(wallet, mockctx)
   873  		require.NoError(t, err)
   874  		require.NotEmpty(t, didcomm)
   875  
   876  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   877  		require.NoError(t, err)
   878  		require.NotEmpty(t, token)
   879  
   880  		defer wallet.Close()
   881  
   882  		response, err := didcomm.PresentProof(token, thID, FromPresentation(&verifiable.Presentation{}),
   883  			WaitForDone(1*time.Millisecond))
   884  		require.NoError(t, err)
   885  		require.NotEmpty(t, response)
   886  		require.Equal(t, model.AckStatusOK, response.Status)
   887  		require.Equal(t, exampleWebRedirect, response.RedirectURL)
   888  	})
   889  
   890  	t.Run("test present proof success - wait for abandoned with redirect", func(t *testing.T) {
   891  		thID := uuid.New().String()
   892  		mockPresentProofSvc := &mockpresentproof.MockPresentProofSvc{
   893  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   894  				ch <- service.StateMsg{
   895  					Type:    service.PostState,
   896  					StateID: presentproofSvc.StateNameAbandoned,
   897  					Properties: &mockdidexchange.MockEventProperties{
   898  						Properties: map[string]interface{}{
   899  							webRedirectStatusKey: model.AckStatusFAIL,
   900  							webRedirectURLKey:    exampleWebRedirect,
   901  						},
   902  					},
   903  					Msg: &mockMsg{thID: thID},
   904  				}
   905  
   906  				return nil
   907  			},
   908  		}
   909  		mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc
   910  
   911  		wallet, err := New(sampleDIDCommUser, mockctx)
   912  		require.NoError(t, err)
   913  		require.NotEmpty(t, wallet)
   914  
   915  		didcomm, err := NewDidComm(wallet, mockctx)
   916  		require.NoError(t, err)
   917  		require.NotEmpty(t, didcomm)
   918  
   919  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   920  		require.NoError(t, err)
   921  		require.NotEmpty(t, token)
   922  
   923  		defer wallet.Close()
   924  
   925  		response, err := didcomm.PresentProof(token, thID, FromPresentation(&verifiable.Presentation{}),
   926  			WaitForDone(1*time.Millisecond))
   927  		require.NoError(t, err)
   928  		require.NotEmpty(t, response)
   929  		require.Equal(t, model.AckStatusFAIL, response.Status)
   930  		require.Equal(t, exampleWebRedirect, response.RedirectURL)
   931  	})
   932  
   933  	t.Run("test present proof success - wait for done no redirect", func(t *testing.T) {
   934  		thID := uuid.New().String()
   935  		mockPresentProofSvc := &mockpresentproof.MockPresentProofSvc{
   936  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   937  				ch <- service.StateMsg{
   938  					Type:       service.PostState,
   939  					StateID:    presentproofSvc.StateNameDone,
   940  					Properties: &mockdidexchange.MockEventProperties{},
   941  					Msg:        &mockMsg{thID: thID},
   942  				}
   943  
   944  				return nil
   945  			},
   946  		}
   947  		mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc
   948  
   949  		wallet, err := New(sampleDIDCommUser, mockctx)
   950  		require.NoError(t, err)
   951  		require.NotEmpty(t, wallet)
   952  
   953  		didcomm, err := NewDidComm(wallet, mockctx)
   954  		require.NoError(t, err)
   955  		require.NotEmpty(t, didcomm)
   956  
   957  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   958  		require.NoError(t, err)
   959  		require.NotEmpty(t, token)
   960  
   961  		defer wallet.Close()
   962  
   963  		response, err := didcomm.PresentProof(token, thID, FromPresentation(&verifiable.Presentation{}),
   964  			WaitForDone(1*time.Millisecond))
   965  		require.NoError(t, err)
   966  		require.NotEmpty(t, response)
   967  		require.Equal(t, model.AckStatusOK, response.Status)
   968  		require.Empty(t, response.RedirectURL)
   969  	})
   970  
   971  	t.Run("test present proof failure - wait for abandoned no redirect", func(t *testing.T) {
   972  		thID := uuid.New().String()
   973  		mockPresentProofSvc := &mockpresentproof.MockPresentProofSvc{
   974  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
   975  				ch <- service.StateMsg{
   976  					Type:       service.PostState,
   977  					StateID:    presentproofSvc.StateNameAbandoned,
   978  					Properties: &mockdidexchange.MockEventProperties{},
   979  					Msg:        &mockMsg{thID: thID},
   980  				}
   981  
   982  				return nil
   983  			},
   984  		}
   985  		mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc
   986  
   987  		wallet, err := New(sampleDIDCommUser, mockctx)
   988  		require.NoError(t, err)
   989  		require.NotEmpty(t, wallet)
   990  
   991  		didcomm, err := NewDidComm(wallet, mockctx)
   992  		require.NoError(t, err)
   993  		require.NotEmpty(t, didcomm)
   994  
   995  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
   996  		require.NoError(t, err)
   997  		require.NotEmpty(t, token)
   998  
   999  		defer wallet.Close()
  1000  
  1001  		response, err := didcomm.PresentProof(token, thID, FromPresentation(&verifiable.Presentation{}),
  1002  			WaitForDone(1*time.Millisecond))
  1003  		require.NoError(t, err)
  1004  		require.NotEmpty(t, response)
  1005  		require.Equal(t, model.AckStatusFAIL, response.Status)
  1006  		require.Empty(t, response.RedirectURL)
  1007  	})
  1008  
  1009  	t.Run("test present proof failure", func(t *testing.T) {
  1010  		ppSvc := &mockpresentproof.MockPresentProofSvc{
  1011  			ActionContinueFunc: func(string, ...presentproofSvc.Opt) error {
  1012  				return fmt.Errorf(sampleWalletErr)
  1013  			},
  1014  		}
  1015  
  1016  		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
  1017  
  1018  		wallet, err := New(sampleDIDCommUser, mockctx)
  1019  		require.NoError(t, err)
  1020  		require.NotEmpty(t, wallet)
  1021  
  1022  		didcomm, err := NewDidComm(wallet, mockctx)
  1023  		require.NoError(t, err)
  1024  		require.NotEmpty(t, didcomm)
  1025  
  1026  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1027  		require.NoError(t, err)
  1028  		require.NotEmpty(t, token)
  1029  
  1030  		defer wallet.Close()
  1031  
  1032  		response, err := didcomm.PresentProof(token, uuid.New().String(), FromRawPresentation([]byte("{}")))
  1033  		require.Error(t, err)
  1034  		require.Contains(t, err.Error(), sampleWalletErr)
  1035  		require.Empty(t, response)
  1036  	})
  1037  
  1038  	t.Run("test present proof failure - failed to register message event", func(t *testing.T) {
  1039  		thID := uuid.New().String()
  1040  		mockPresentProofSvc := &mockpresentproof.MockPresentProofSvc{
  1041  			RegisterMsgEventErr: errors.New(sampleWalletErr),
  1042  		}
  1043  		mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc
  1044  
  1045  		wallet, err := New(sampleDIDCommUser, mockctx)
  1046  		require.NoError(t, err)
  1047  		require.NotEmpty(t, wallet)
  1048  
  1049  		didcomm, err := NewDidComm(wallet, mockctx)
  1050  		require.NoError(t, err)
  1051  		require.NotEmpty(t, didcomm)
  1052  
  1053  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1054  		require.NoError(t, err)
  1055  		require.NotEmpty(t, token)
  1056  
  1057  		defer wallet.Close()
  1058  
  1059  		response, err := didcomm.PresentProof(token, thID, FromPresentation(&verifiable.Presentation{}),
  1060  			WaitForDone(1*time.Millisecond))
  1061  		require.Error(t, err)
  1062  		require.Contains(t, err.Error(), sampleWalletErr)
  1063  		require.Empty(t, response)
  1064  	})
  1065  
  1066  	t.Run("test present proof failure - wait for done timeout", func(t *testing.T) {
  1067  		thID := uuid.New().String()
  1068  		mockPresentProofSvc := &mockpresentproof.MockPresentProofSvc{
  1069  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1070  				ch <- service.StateMsg{
  1071  					Type: service.PreState,
  1072  				}
  1073  
  1074  				ch <- service.StateMsg{
  1075  					Type: service.PostState,
  1076  				}
  1077  
  1078  				ch <- service.StateMsg{
  1079  					Type: service.PostState,
  1080  					Msg:  &mockMsg{thID: "invalid"},
  1081  				}
  1082  
  1083  				ch <- service.StateMsg{
  1084  					Type:    service.PostState,
  1085  					StateID: "invalid",
  1086  					Msg:     &mockMsg{thID: thID, fail: errors.New(sampleWalletErr)},
  1087  				}
  1088  
  1089  				ch <- service.StateMsg{
  1090  					Type:    service.PostState,
  1091  					StateID: "invalid",
  1092  					Msg:     &mockMsg{thID: thID},
  1093  				}
  1094  
  1095  				return nil
  1096  			},
  1097  			UnregisterMsgEventErr: errors.New(sampleWalletErr),
  1098  		}
  1099  		mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc
  1100  
  1101  		wallet, err := New(sampleDIDCommUser, mockctx)
  1102  		require.NoError(t, err)
  1103  		require.NotEmpty(t, wallet)
  1104  
  1105  		didcomm, err := NewDidComm(wallet, mockctx)
  1106  		require.NoError(t, err)
  1107  		require.NotEmpty(t, didcomm)
  1108  
  1109  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1110  		require.NoError(t, err)
  1111  		require.NotEmpty(t, token)
  1112  
  1113  		defer wallet.Close()
  1114  
  1115  		response, err := didcomm.PresentProof(token, thID, FromPresentation(&verifiable.Presentation{}),
  1116  			WaitForDone(1*time.Millisecond))
  1117  		require.Error(t, err)
  1118  		require.Contains(t, err.Error(), "time out waiting for credential interaction to get completed")
  1119  		require.Empty(t, response)
  1120  	})
  1121  }
  1122  
  1123  func TestWallet_ProposeCredential(t *testing.T) {
  1124  	sampleDIDCommUser := uuid.New().String()
  1125  	mockctx := newDidCommMockProvider(t)
  1126  	err := CreateProfile(sampleDIDCommUser, mockctx, WithPassphrase(samplePassPhrase))
  1127  	require.NoError(t, err)
  1128  
  1129  	const (
  1130  		myDID    = "did:mydid:123"
  1131  		theirDID = "did:theirdid:123"
  1132  	)
  1133  
  1134  	t.Run("test propose credential success", func(t *testing.T) {
  1135  		sampleConnID := uuid.New().String()
  1136  
  1137  		oobSvc := &mockoutofband.MockOobService{
  1138  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
  1139  				return sampleConnID, nil
  1140  			},
  1141  		}
  1142  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
  1143  
  1144  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
  1145  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1146  				ch <- service.StateMsg{
  1147  					Type:       service.PostState,
  1148  					StateID:    didexchange.StateIDCompleted,
  1149  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
  1150  				}
  1151  
  1152  				return nil
  1153  			},
  1154  		}
  1155  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
  1156  
  1157  		thID := uuid.New().String()
  1158  
  1159  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1160  			ActionsFunc: func() ([]issuecredentialsvc.Action, error) {
  1161  				return []issuecredentialsvc.Action{
  1162  					{
  1163  						PIID: thID,
  1164  						Msg: service.NewDIDCommMsgMap(&issuecredentialsvc.OfferCredentialV2{
  1165  							Comment: sampleMsgComment,
  1166  						}),
  1167  						MyDID:    myDID,
  1168  						TheirDID: theirDID,
  1169  					},
  1170  				}, nil
  1171  			},
  1172  			HandleFunc: func(service.DIDCommMsg) (string, error) {
  1173  				return thID, nil
  1174  			},
  1175  		}
  1176  
  1177  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
  1178  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1179  
  1180  		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
  1181  		require.NoError(t, err)
  1182  
  1183  		record := &connection.Record{
  1184  			ConnectionID: sampleConnID,
  1185  			MyDID:        myDID,
  1186  			TheirDID:     theirDID,
  1187  		}
  1188  		recordBytes, err := json.Marshal(record)
  1189  		require.NoError(t, err)
  1190  		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
  1191  
  1192  		wallet, err := New(sampleDIDCommUser, mockctx)
  1193  		require.NoError(t, err)
  1194  		require.NotEmpty(t, wallet)
  1195  
  1196  		didcomm, err := NewDidComm(wallet, mockctx)
  1197  		require.NoError(t, err)
  1198  		require.NotEmpty(t, didcomm)
  1199  
  1200  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1201  		require.NoError(t, err)
  1202  		require.NotEmpty(t, token)
  1203  
  1204  		defer wallet.Close()
  1205  
  1206  		msg, err := didcomm.ProposeCredential(token, &GenericInvitation{},
  1207  			WithConnectOptions(WithConnectTimeout(1*time.Millisecond)))
  1208  		require.NoError(t, err)
  1209  		require.NotEmpty(t, msg)
  1210  
  1211  		offer := &issuecredentialsvc.OfferCredentialV2{}
  1212  
  1213  		err = msg.Decode(offer)
  1214  		require.NoError(t, err)
  1215  		require.NotEmpty(t, offer)
  1216  		require.Equal(t, sampleMsgComment, offer.Comment)
  1217  	})
  1218  
  1219  	t.Run("test propose credential failure - did connect failure", func(t *testing.T) {
  1220  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
  1221  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1222  				return fmt.Errorf(sampleWalletErr)
  1223  			},
  1224  		}
  1225  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
  1226  
  1227  		wallet, err := New(sampleDIDCommUser, mockctx)
  1228  		require.NoError(t, err)
  1229  		require.NotEmpty(t, wallet)
  1230  
  1231  		didcomm, err := NewDidComm(wallet, mockctx)
  1232  		require.NoError(t, err)
  1233  		require.NotEmpty(t, didcomm)
  1234  
  1235  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1236  		require.NoError(t, err)
  1237  		require.NotEmpty(t, token)
  1238  
  1239  		defer wallet.Close()
  1240  
  1241  		msg, err := didcomm.ProposeCredential(token, &GenericInvitation{})
  1242  		require.Error(t, err)
  1243  		require.Contains(t, err.Error(), sampleWalletErr)
  1244  		require.Contains(t, err.Error(), "failed to perform did connection")
  1245  		require.Empty(t, msg)
  1246  	})
  1247  
  1248  	t.Run("test propose credential failure - oobv2 accept error", func(t *testing.T) {
  1249  		ctrl := gomock.NewController(t)
  1250  		defer ctrl.Finish()
  1251  
  1252  		expectErr := fmt.Errorf("expected error")
  1253  
  1254  		oobv2Svc := mockoutofbandv2.NewMockOobService(ctrl)
  1255  		oobv2Svc.EXPECT().AcceptInvitation(gomock.Any(), gomock.Any()).Return("", expectErr).AnyTimes()
  1256  
  1257  		mockctx.ServiceMap[oobv2.Name] = oobv2Svc
  1258  
  1259  		wallet, err := New(sampleDIDCommUser, mockctx)
  1260  		require.NoError(t, err)
  1261  		require.NotEmpty(t, wallet)
  1262  
  1263  		didcomm, err := NewDidComm(wallet, mockctx)
  1264  		require.NoError(t, err)
  1265  		require.NotEmpty(t, didcomm)
  1266  
  1267  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1268  		require.NoError(t, err)
  1269  		require.NotEmpty(t, token)
  1270  
  1271  		defer wallet.Close()
  1272  
  1273  		invitation := GenericInvitation{}
  1274  		err = json.Unmarshal([]byte(`{
  1275  			"id": "abc123",
  1276  			"type": "https://didcomm.org/out-of-band/2.0/invitation"
  1277  		}`), &invitation)
  1278  		require.NoError(t, err)
  1279  
  1280  		_, err = didcomm.ProposeCredential(token, &invitation,
  1281  			WithConnectOptions(WithConnectTimeout(1*time.Millisecond)))
  1282  		require.Error(t, err)
  1283  		require.ErrorIs(t, err, expectErr)
  1284  		require.Contains(t, err.Error(), "failed to accept OOB v2 invitation")
  1285  	})
  1286  
  1287  	t.Run("test propose credential failure - no connection found", func(t *testing.T) {
  1288  		sampleConnID := uuid.New().String()
  1289  
  1290  		oobSvc := &mockoutofband.MockOobService{
  1291  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
  1292  				return sampleConnID, nil
  1293  			},
  1294  		}
  1295  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
  1296  
  1297  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
  1298  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1299  				ch <- service.StateMsg{
  1300  					Type:       service.PostState,
  1301  					StateID:    didexchange.StateIDCompleted,
  1302  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
  1303  				}
  1304  
  1305  				return nil
  1306  			},
  1307  		}
  1308  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
  1309  
  1310  		wallet, err := New(sampleDIDCommUser, mockctx)
  1311  		require.NoError(t, err)
  1312  		require.NotEmpty(t, wallet)
  1313  
  1314  		didcomm, err := NewDidComm(wallet, mockctx)
  1315  		require.NoError(t, err)
  1316  		require.NotEmpty(t, didcomm)
  1317  
  1318  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1319  		require.NoError(t, err)
  1320  		require.NotEmpty(t, token)
  1321  
  1322  		defer wallet.Close()
  1323  
  1324  		msg, err := didcomm.ProposeCredential(token, &GenericInvitation{})
  1325  		require.Error(t, err)
  1326  		require.Contains(t, err.Error(), "failed to lookup connection")
  1327  		require.Empty(t, msg)
  1328  	})
  1329  
  1330  	t.Run("test propose credential failure - failed to send", func(t *testing.T) {
  1331  		sampleConnID := uuid.New().String()
  1332  
  1333  		oobSvc := &mockoutofband.MockOobService{
  1334  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
  1335  				return sampleConnID, nil
  1336  			},
  1337  		}
  1338  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
  1339  
  1340  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
  1341  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1342  				ch <- service.StateMsg{
  1343  					Type:       service.PostState,
  1344  					StateID:    didexchange.StateIDCompleted,
  1345  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
  1346  				}
  1347  
  1348  				return nil
  1349  			},
  1350  		}
  1351  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
  1352  
  1353  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1354  			HandleFunc: func(service.DIDCommMsg) (string, error) {
  1355  				return "", fmt.Errorf(sampleWalletErr)
  1356  			},
  1357  			HandleOutboundFunc: func(service.DIDCommMsg, string, string) (string, error) {
  1358  				return "", fmt.Errorf(sampleWalletErr)
  1359  			},
  1360  		}
  1361  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1362  
  1363  		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
  1364  		require.NoError(t, err)
  1365  
  1366  		record := &connection.Record{
  1367  			ConnectionID: sampleConnID,
  1368  			MyDID:        myDID,
  1369  			TheirDID:     theirDID,
  1370  		}
  1371  		recordBytes, err := json.Marshal(record)
  1372  		require.NoError(t, err)
  1373  		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
  1374  
  1375  		wallet, err := New(sampleDIDCommUser, mockctx)
  1376  		require.NoError(t, err)
  1377  		require.NotEmpty(t, wallet)
  1378  
  1379  		didcomm, err := NewDidComm(wallet, mockctx)
  1380  		require.NoError(t, err)
  1381  		require.NotEmpty(t, didcomm)
  1382  
  1383  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1384  		require.NoError(t, err)
  1385  		require.NotEmpty(t, token)
  1386  
  1387  		defer wallet.Close()
  1388  
  1389  		msg, err := didcomm.ProposeCredential(token, &GenericInvitation{})
  1390  		require.Error(t, err)
  1391  		require.Contains(t, err.Error(), sampleWalletErr)
  1392  		require.Contains(t, err.Error(), "failed to propose credential from wallet")
  1393  		require.Empty(t, msg)
  1394  	})
  1395  
  1396  	t.Run("test propose credential failure - timeout waiting for offer credential msg", func(t *testing.T) {
  1397  		sampleConnID := uuid.New().String()
  1398  
  1399  		oobSvc := &mockoutofband.MockOobService{
  1400  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
  1401  				return sampleConnID, nil
  1402  			},
  1403  		}
  1404  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
  1405  
  1406  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
  1407  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1408  				ch <- service.StateMsg{
  1409  					Type:       service.PostState,
  1410  					StateID:    didexchange.StateIDCompleted,
  1411  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
  1412  				}
  1413  
  1414  				return nil
  1415  			},
  1416  		}
  1417  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
  1418  
  1419  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1420  			HandleFunc: func(service.DIDCommMsg) (string, error) {
  1421  				return uuid.New().String(), nil
  1422  			},
  1423  		}
  1424  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1425  
  1426  		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
  1427  		require.NoError(t, err)
  1428  
  1429  		record := &connection.Record{
  1430  			ConnectionID: sampleConnID,
  1431  			MyDID:        myDID,
  1432  			TheirDID:     theirDID,
  1433  		}
  1434  		recordBytes, err := json.Marshal(record)
  1435  		require.NoError(t, err)
  1436  		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
  1437  
  1438  		wallet, err := New(sampleDIDCommUser, mockctx)
  1439  		require.NoError(t, err)
  1440  		require.NotEmpty(t, wallet)
  1441  
  1442  		didcomm, err := NewDidComm(wallet, mockctx)
  1443  		require.NoError(t, err)
  1444  		require.NotEmpty(t, didcomm)
  1445  
  1446  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1447  		require.NoError(t, err)
  1448  		require.NotEmpty(t, token)
  1449  
  1450  		defer wallet.Close()
  1451  
  1452  		msg, err := didcomm.ProposeCredential(token, &GenericInvitation{}, WithInitiateTimeout(600*time.Millisecond))
  1453  		require.Error(t, err)
  1454  		require.Contains(t, err.Error(), "timeout waiting for offer credential message")
  1455  		require.Empty(t, msg)
  1456  	})
  1457  
  1458  	t.Run("test propose presentation failure - action error", func(t *testing.T) {
  1459  		sampleConnID := uuid.New().String()
  1460  
  1461  		oobSvc := &mockoutofband.MockOobService{
  1462  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
  1463  				return sampleConnID, nil
  1464  			},
  1465  		}
  1466  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
  1467  
  1468  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
  1469  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1470  				ch <- service.StateMsg{
  1471  					Type:       service.PostState,
  1472  					StateID:    didexchange.StateIDCompleted,
  1473  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
  1474  				}
  1475  
  1476  				return nil
  1477  			},
  1478  		}
  1479  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
  1480  
  1481  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1482  			HandleFunc: func(service.DIDCommMsg) (string, error) {
  1483  				return uuid.New().String(), nil
  1484  			},
  1485  			ActionsFunc: func() ([]issuecredentialsvc.Action, error) {
  1486  				return nil, fmt.Errorf(sampleWalletErr)
  1487  			},
  1488  		}
  1489  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1490  
  1491  		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
  1492  		require.NoError(t, err)
  1493  
  1494  		record := &connection.Record{
  1495  			ConnectionID: sampleConnID,
  1496  			TheirDID:     theirDID,
  1497  		}
  1498  		recordBytes, err := json.Marshal(record)
  1499  		require.NoError(t, err)
  1500  		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
  1501  
  1502  		wallet, err := New(sampleDIDCommUser, mockctx)
  1503  		require.NoError(t, err)
  1504  		require.NotEmpty(t, wallet)
  1505  
  1506  		didcomm, err := NewDidComm(wallet, mockctx)
  1507  		require.NoError(t, err)
  1508  		require.NotEmpty(t, didcomm)
  1509  
  1510  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1511  		require.NoError(t, err)
  1512  		require.NotEmpty(t, token)
  1513  
  1514  		defer wallet.Close()
  1515  
  1516  		msg, err := didcomm.ProposeCredential(token, &GenericInvitation{}, WithInitiateTimeout(1*time.Millisecond),
  1517  			WithFromDID("did:sample:from"))
  1518  		require.Error(t, err)
  1519  		require.Contains(t, err.Error(), "timeout waiting for offer credential message")
  1520  		require.Empty(t, msg)
  1521  	})
  1522  }
  1523  
  1524  func TestWallet_RequestCredential(t *testing.T) {
  1525  	sampleDIDCommUser := uuid.New().String()
  1526  	mockctx := newDidCommMockProvider(t)
  1527  	err := CreateProfile(sampleDIDCommUser, mockctx, WithPassphrase(samplePassPhrase))
  1528  	require.NoError(t, err)
  1529  
  1530  	t.Run("test request credential success", func(t *testing.T) {
  1531  		wallet, err := New(sampleDIDCommUser, mockctx)
  1532  		require.NoError(t, err)
  1533  		require.NotEmpty(t, wallet)
  1534  
  1535  		didcomm, err := NewDidComm(wallet, mockctx)
  1536  		require.NoError(t, err)
  1537  		require.NotEmpty(t, didcomm)
  1538  
  1539  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1540  		require.NoError(t, err)
  1541  		require.NotEmpty(t, token)
  1542  
  1543  		defer wallet.Close()
  1544  
  1545  		response, err := didcomm.RequestCredential(token, uuid.New().String(), FromPresentation(&verifiable.Presentation{}))
  1546  		require.NoError(t, err)
  1547  		require.NotEmpty(t, response)
  1548  		require.Equal(t, model.AckStatusPENDING, response.Status)
  1549  	})
  1550  
  1551  	t.Run("test request credential success - wait for done with redirect", func(t *testing.T) {
  1552  		thID := uuid.New().String()
  1553  
  1554  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1555  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1556  				ch <- service.StateMsg{
  1557  					Type:    service.PostState,
  1558  					StateID: stateNameDone,
  1559  					Properties: &mockdidexchange.MockEventProperties{
  1560  						Properties: map[string]interface{}{
  1561  							webRedirectStatusKey: model.AckStatusOK,
  1562  							webRedirectURLKey:    exampleWebRedirect,
  1563  						},
  1564  					},
  1565  					Msg: &mockMsg{thID: thID},
  1566  				}
  1567  
  1568  				return nil
  1569  			},
  1570  		}
  1571  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1572  
  1573  		wallet, err := New(sampleDIDCommUser, mockctx)
  1574  		require.NoError(t, err)
  1575  		require.NotEmpty(t, wallet)
  1576  
  1577  		didcomm, err := NewDidComm(wallet, mockctx)
  1578  		require.NoError(t, err)
  1579  		require.NotEmpty(t, didcomm)
  1580  
  1581  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1582  		require.NoError(t, err)
  1583  		require.NotEmpty(t, token)
  1584  
  1585  		defer wallet.Close()
  1586  
  1587  		response, err := didcomm.RequestCredential(token, thID, FromPresentation(&verifiable.Presentation{}),
  1588  			WaitForDone(0))
  1589  		require.NoError(t, err)
  1590  		require.NotEmpty(t, response)
  1591  		require.Equal(t, model.AckStatusOK, response.Status)
  1592  		require.Equal(t, exampleWebRedirect, response.RedirectURL)
  1593  	})
  1594  
  1595  	t.Run("test for request credential - wait for problem report with redirect", func(t *testing.T) {
  1596  		thID := uuid.New().String()
  1597  
  1598  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1599  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1600  				ch <- service.StateMsg{
  1601  					Type:    service.PostState,
  1602  					StateID: stateNameAbandoned,
  1603  					Properties: &mockdidexchange.MockEventProperties{
  1604  						Properties: map[string]interface{}{
  1605  							webRedirectStatusKey: model.AckStatusFAIL,
  1606  							webRedirectURLKey:    exampleWebRedirect,
  1607  						},
  1608  					},
  1609  					Msg: &mockMsg{thID: thID},
  1610  				}
  1611  
  1612  				return nil
  1613  			},
  1614  		}
  1615  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1616  
  1617  		wallet, err := New(sampleDIDCommUser, mockctx)
  1618  		require.NoError(t, err)
  1619  		require.NotEmpty(t, wallet)
  1620  
  1621  		didcomm, err := NewDidComm(wallet, mockctx)
  1622  		require.NoError(t, err)
  1623  		require.NotEmpty(t, didcomm)
  1624  
  1625  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1626  		require.NoError(t, err)
  1627  		require.NotEmpty(t, token)
  1628  
  1629  		defer wallet.Close()
  1630  
  1631  		response, err := didcomm.RequestCredential(token, thID, FromPresentation(&verifiable.Presentation{}),
  1632  			WaitForDone(1*time.Millisecond))
  1633  		require.NoError(t, err)
  1634  		require.NotEmpty(t, response)
  1635  		require.Equal(t, model.AckStatusFAIL, response.Status)
  1636  		require.Equal(t, exampleWebRedirect, response.RedirectURL)
  1637  	})
  1638  
  1639  	t.Run("test request credential success - wait for done no redirect", func(t *testing.T) {
  1640  		thID := uuid.New().String()
  1641  
  1642  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1643  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1644  				ch <- service.StateMsg{
  1645  					Type:       service.PostState,
  1646  					StateID:    stateNameDone,
  1647  					Properties: &mockdidexchange.MockEventProperties{},
  1648  					Msg:        &mockMsg{thID: thID},
  1649  				}
  1650  
  1651  				return nil
  1652  			},
  1653  		}
  1654  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1655  
  1656  		wallet, err := New(sampleDIDCommUser, mockctx)
  1657  		require.NoError(t, err)
  1658  		require.NotEmpty(t, wallet)
  1659  
  1660  		didcomm, err := NewDidComm(wallet, mockctx)
  1661  		require.NoError(t, err)
  1662  		require.NotEmpty(t, didcomm)
  1663  
  1664  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1665  		require.NoError(t, err)
  1666  		require.NotEmpty(t, token)
  1667  
  1668  		defer wallet.Close()
  1669  
  1670  		response, err := didcomm.RequestCredential(token, thID, FromPresentation(&verifiable.Presentation{}),
  1671  			WaitForDone(10*time.Millisecond))
  1672  		require.NoError(t, err)
  1673  		require.NotEmpty(t, response)
  1674  		require.Equal(t, model.AckStatusOK, response.Status)
  1675  		require.Empty(t, response.RedirectURL)
  1676  	})
  1677  
  1678  	t.Run("test request credential failure - wait for problem report no redirect", func(t *testing.T) {
  1679  		thID := uuid.New().String()
  1680  
  1681  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1682  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1683  				ch <- service.StateMsg{
  1684  					Type:       service.PostState,
  1685  					StateID:    stateNameAbandoned,
  1686  					Properties: &mockdidexchange.MockEventProperties{},
  1687  					Msg:        &mockMsg{thID: thID},
  1688  				}
  1689  
  1690  				return nil
  1691  			},
  1692  		}
  1693  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1694  
  1695  		wallet, err := New(sampleDIDCommUser, mockctx)
  1696  		require.NoError(t, err)
  1697  		require.NotEmpty(t, wallet)
  1698  
  1699  		didcomm, err := NewDidComm(wallet, mockctx)
  1700  		require.NoError(t, err)
  1701  		require.NotEmpty(t, didcomm)
  1702  
  1703  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1704  		require.NoError(t, err)
  1705  		require.NotEmpty(t, token)
  1706  
  1707  		defer wallet.Close()
  1708  
  1709  		response, err := didcomm.RequestCredential(token, thID, FromPresentation(&verifiable.Presentation{}),
  1710  			WaitForDone(1*time.Millisecond))
  1711  		require.NoError(t, err)
  1712  		require.NotEmpty(t, response)
  1713  		require.Equal(t, model.AckStatusFAIL, response.Status)
  1714  		require.Empty(t, response.RedirectURL)
  1715  	})
  1716  
  1717  	t.Run("test request credential failure", func(t *testing.T) {
  1718  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1719  			ActionContinueFunc: func(string, ...issuecredentialsvc.Opt) error {
  1720  				return fmt.Errorf(sampleWalletErr)
  1721  			},
  1722  		}
  1723  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1724  
  1725  		wallet, err := New(sampleDIDCommUser, mockctx)
  1726  		require.NoError(t, err)
  1727  		require.NotEmpty(t, wallet)
  1728  
  1729  		didcomm, err := NewDidComm(wallet, mockctx)
  1730  		require.NoError(t, err)
  1731  		require.NotEmpty(t, didcomm)
  1732  
  1733  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1734  		require.NoError(t, err)
  1735  		require.NotEmpty(t, token)
  1736  
  1737  		defer wallet.Close()
  1738  
  1739  		response, err := didcomm.RequestCredential(token, uuid.New().String(), FromRawPresentation([]byte("{}")))
  1740  		require.Error(t, err)
  1741  		require.Contains(t, err.Error(), sampleWalletErr)
  1742  		require.Empty(t, response)
  1743  	})
  1744  
  1745  	t.Run("test request credential failure - failed to register msg event", func(t *testing.T) {
  1746  		thID := uuid.New().String()
  1747  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1748  			RegisterMsgEventErr: errors.New(sampleWalletErr),
  1749  		}
  1750  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1751  
  1752  		wallet, err := New(sampleDIDCommUser, mockctx)
  1753  		require.NoError(t, err)
  1754  		require.NotEmpty(t, wallet)
  1755  
  1756  		didcomm, err := NewDidComm(wallet, mockctx)
  1757  		require.NoError(t, err)
  1758  		require.NotEmpty(t, didcomm)
  1759  
  1760  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1761  		require.NoError(t, err)
  1762  		require.NotEmpty(t, token)
  1763  
  1764  		defer wallet.Close()
  1765  
  1766  		response, err := didcomm.RequestCredential(token, thID, FromPresentation(&verifiable.Presentation{}),
  1767  			WaitForDone(1*time.Millisecond))
  1768  		require.Error(t, err)
  1769  		require.Contains(t, err.Error(), sampleWalletErr)
  1770  		require.Empty(t, response)
  1771  	})
  1772  
  1773  	t.Run("test request credential success - wait for done timeout", func(t *testing.T) {
  1774  		thID := uuid.New().String()
  1775  
  1776  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1777  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1778  				ch <- service.StateMsg{
  1779  					Type: service.PreState,
  1780  				}
  1781  
  1782  				ch <- service.StateMsg{
  1783  					Type: service.PostState,
  1784  				}
  1785  
  1786  				ch <- service.StateMsg{
  1787  					Type: service.PostState,
  1788  					Msg:  &mockMsg{thID: "invalid"},
  1789  				}
  1790  
  1791  				ch <- service.StateMsg{
  1792  					Type:    service.PostState,
  1793  					StateID: "invalid",
  1794  					Msg:     &mockMsg{thID: thID, fail: errors.New(sampleWalletErr)},
  1795  				}
  1796  
  1797  				ch <- service.StateMsg{
  1798  					Type:    service.PostState,
  1799  					StateID: "invalid",
  1800  					Msg:     &mockMsg{thID: thID},
  1801  				}
  1802  
  1803  				return nil
  1804  			},
  1805  			UnregisterMsgEventErr: errors.New(sampleWalletErr),
  1806  		}
  1807  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1808  
  1809  		wallet, err := New(sampleDIDCommUser, mockctx)
  1810  		require.NoError(t, err)
  1811  		require.NotEmpty(t, wallet)
  1812  
  1813  		didcomm, err := NewDidComm(wallet, mockctx)
  1814  		require.NoError(t, err)
  1815  		require.NotEmpty(t, didcomm)
  1816  
  1817  		token, err := wallet.Open(WithUnlockByPassphrase(samplePassPhrase))
  1818  		require.NoError(t, err)
  1819  		require.NotEmpty(t, token)
  1820  
  1821  		defer wallet.Close()
  1822  
  1823  		response, err := didcomm.RequestCredential(token, thID, FromPresentation(&verifiable.Presentation{}),
  1824  			WaitForDone(700*time.Millisecond))
  1825  		require.Error(t, err)
  1826  		require.Contains(t, err.Error(), "time out waiting for credential interaction to get completed")
  1827  		require.Empty(t, response)
  1828  	})
  1829  }
  1830  
  1831  func newDidCommMockProvider(t *testing.T) *mockprovider.Provider {
  1832  	t.Helper()
  1833  
  1834  	loader, err := ldtestutil.DocumentLoader()
  1835  	require.NoError(t, err)
  1836  
  1837  	serviceMap := map[string]interface{}{
  1838  		presentproofSvc.Name:    &mockpresentproof.MockPresentProofSvc{},
  1839  		outofbandSvc.Name:       &mockoutofband.MockOobService{},
  1840  		didexchange.DIDExchange: &mockdidexchange.MockDIDExchangeSvc{},
  1841  		mediator.Coordination:   &mockmediator.MockMediatorSvc{},
  1842  		issuecredentialsvc.Name: &mockissuecredential.MockIssueCredentialSvc{},
  1843  		oobv2.Name:              &mockoutofbandv2.MockOobService{},
  1844  	}
  1845  
  1846  	return &mockprovider.Provider{
  1847  		StorageProviderValue:              mockstorage.NewMockStoreProvider(),
  1848  		ProtocolStateStorageProviderValue: mockstorage.NewMockStoreProvider(),
  1849  		DocumentLoaderValue:               loader,
  1850  		ServiceMap:                        serviceMap,
  1851  	}
  1852  }
  1853  
  1854  // mockMsg containing custom parent thread ID.
  1855  type mockMsg struct {
  1856  	*service.DIDCommMsgMap
  1857  	thID    string
  1858  	fail    error
  1859  	msgType string
  1860  }
  1861  
  1862  func (m *mockMsg) ParentThreadID() string {
  1863  	return m.thID
  1864  }
  1865  
  1866  func (m *mockMsg) ThreadID() (string, error) {
  1867  	return m.thID, m.fail
  1868  }
  1869  
  1870  func (m *mockMsg) Type() string {
  1871  	if m.msgType != "" {
  1872  		return m.msgType
  1873  	}
  1874  
  1875  	if m.DIDCommMsgMap != nil {
  1876  		return m.DIDCommMsgMap.Type()
  1877  	}
  1878  
  1879  	return ""
  1880  }