github.com/hyperledger/aries-framework-go@v0.3.2/pkg/store/connection/connection_recorder_test.go (about)

     1  /*
     2   *
     3   * Copyright SecureKey Technologies Inc. All Rights Reserved.
     4   *
     5   * SPDX-License-Identifier: Apache-2.0
     6   * /
     7   *
     8   */
     9  
    10  package connection
    11  
    12  import (
    13  	"fmt"
    14  	"testing"
    15  
    16  	"github.com/google/uuid"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator"
    20  	mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage"
    21  	"github.com/hyperledger/aries-framework-go/spi/storage"
    22  )
    23  
    24  const (
    25  	threadIDValue    = "xyz"
    26  	sampleConnID     = "sample-conn-ID"
    27  	stateNameInvited = "invited"
    28  )
    29  
    30  func Test_NewConnectionRecorder(t *testing.T) {
    31  	t.Run("create new recorder - success", func(t *testing.T) {
    32  		recorder, err := NewRecorder(&mockProvider{})
    33  		require.NoError(t, err)
    34  		require.NotNil(t, recorder)
    35  	})
    36  
    37  	t.Run("create new connection recorder - protocol state store error", func(t *testing.T) {
    38  		lookup, err := NewRecorder(&mockProvider{protocolStateStoreError: fmt.Errorf(sampleErrMsg)})
    39  		require.Error(t, err)
    40  		require.Contains(t, err.Error(), sampleErrMsg)
    41  		require.Nil(t, lookup)
    42  	})
    43  
    44  	t.Run("create new connection recorder - permanent store error", func(t *testing.T) {
    45  		lookup, err := NewRecorder(&mockProvider{storeError: fmt.Errorf(sampleErrMsg)})
    46  		require.Error(t, err)
    47  		require.Contains(t, err.Error(), sampleErrMsg)
    48  		require.Nil(t, lookup)
    49  	})
    50  }
    51  
    52  func Test_ComputeHash(t *testing.T) {
    53  	h1, err := computeHash([]byte("sample-bytes-123"))
    54  	require.NoError(t, err)
    55  	require.NotEmpty(t, h1)
    56  
    57  	h2, err := computeHash([]byte("sample-bytes-321"))
    58  	require.NoError(t, err)
    59  	require.NotEmpty(t, h2)
    60  
    61  	h3, err := computeHash([]byte("sample-bytes-123"))
    62  	require.NoError(t, err)
    63  	require.NotEmpty(t, h1)
    64  
    65  	require.NotEqual(t, h1, h2)
    66  	require.Equal(t, h1, h3)
    67  
    68  	h4, err := computeHash([]byte(""))
    69  	require.Error(t, err)
    70  	require.Empty(t, h4)
    71  }
    72  
    73  func Test_RemoveMappings(t *testing.T) {
    74  	t.Run("test success", func(t *testing.T) {
    75  		recorder, err := NewRecorder(&mockProvider{})
    76  		require.NoError(t, err)
    77  		require.NotNil(t, recorder)
    78  
    79  		record := &Record{
    80  			ThreadID: threadIDValue,
    81  		}
    82  
    83  		err = removeMappings(recorder, record)
    84  		require.NoError(t, err)
    85  	})
    86  	t.Run("test failed - empty bytes", func(t *testing.T) {
    87  		recorder, err := NewRecorder(&mockProvider{})
    88  		require.NoError(t, err)
    89  		require.NotNil(t, recorder)
    90  
    91  		record := &Record{
    92  			ThreadID: "",
    93  		}
    94  
    95  		err = removeMappings(recorder, record)
    96  		require.Error(t, err)
    97  		require.Contains(t, err.Error(), "empty bytes")
    98  	})
    99  	t.Run("test failed to delete the record", func(t *testing.T) {
   100  		const errMsg = "get error"
   101  		recorder, err := NewRecorder(&mockProvider{
   102  			store: &mockstorage.MockStore{
   103  				Store:     make(map[string]mockstorage.DBEntry),
   104  				ErrDelete: fmt.Errorf(errMsg),
   105  			},
   106  		})
   107  		require.NoError(t, err)
   108  		require.NotNil(t, recorder)
   109  
   110  		record := &Record{
   111  			ThreadID: threadIDValue,
   112  		}
   113  
   114  		err = removeMappings(recorder, record)
   115  		require.Error(t, err)
   116  		require.Contains(t, err.Error(), errMsg)
   117  	})
   118  }
   119  
   120  func Test_RemoveConnectionRecordsForStates(t *testing.T) {
   121  	t.Run("test success", func(t *testing.T) {
   122  		record := &Record{
   123  			ThreadID:     threadIDValue,
   124  			ConnectionID: uuid.New().String(),
   125  			State:        StateNameCompleted,
   126  			Namespace:    TheirNSPrefix,
   127  			MyDID:        "did:mydid:123",
   128  			TheirDID:     "did:theirdid:123",
   129  		}
   130  
   131  		store := &mockstorage.MockStore{
   132  			Store: make(map[string]mockstorage.DBEntry),
   133  		}
   134  
   135  		err := marshalAndSave(getConnectionStateKeyPrefix()(record.ConnectionID, record.State),
   136  			record, store)
   137  		require.NoError(t, err)
   138  
   139  		recorder, err := NewRecorder(&mockProvider{
   140  			protocolStateStore: store,
   141  		})
   142  		require.NoError(t, err)
   143  		require.NotNil(t, recorder)
   144  
   145  		err = removeConnectionsForStates(recorder, record.ConnectionID)
   146  		require.NoError(t, err)
   147  	})
   148  	t.Run("test failed to delete connection state record from the store", func(t *testing.T) {
   149  		const errMsg = "get error"
   150  		record := &Record{
   151  			ThreadID:     threadIDValue,
   152  			ConnectionID: uuid.New().String(),
   153  			State:        StateNameCompleted,
   154  			Namespace:    TheirNSPrefix,
   155  			MyDID:        "did:mydid:123",
   156  			TheirDID:     "did:theirdid:123",
   157  		}
   158  
   159  		store := &mockstorage.MockStore{
   160  			Store:     make(map[string]mockstorage.DBEntry),
   161  			ErrDelete: fmt.Errorf(errMsg),
   162  		}
   163  
   164  		err := marshalAndSave(getConnectionStateKeyPrefix()(record.ConnectionID, record.State),
   165  			record, store, storage.Tag{
   166  				Name:  connStateKeyPrefix,
   167  				Value: getConnectionStateKeyPrefix()(record.ConnectionID),
   168  			})
   169  		require.NoError(t, err)
   170  
   171  		recorder, err := NewRecorder(&mockProvider{
   172  			protocolStateStore: store,
   173  		})
   174  		require.NoError(t, err)
   175  		require.NotNil(t, recorder)
   176  
   177  		err = removeConnectionsForStates(recorder, record.ConnectionID)
   178  		require.Error(t, err)
   179  		require.Contains(t, err.Error(), errMsg)
   180  	})
   181  }
   182  
   183  func TestConnectionStore_SaveInvitation(t *testing.T) {
   184  	const id = "sample-inv-id"
   185  
   186  	t.Run("test save invitation success", func(t *testing.T) {
   187  		store := &mockstorage.MockStore{Store: make(map[string]mockstorage.DBEntry)}
   188  		recorder, err := NewRecorder(&mockProvider{
   189  			store: store,
   190  		})
   191  		require.NoError(t, err)
   192  
   193  		require.NotNil(t, recorder)
   194  
   195  		value := &mockInvitation{
   196  			ID:    id,
   197  			Label: "sample-label1",
   198  		}
   199  
   200  		err = recorder.SaveInvitation(value.ID, value)
   201  		require.NoError(t, err)
   202  
   203  		require.NotEmpty(t, store)
   204  
   205  		k := getInvitationKeyPrefix()(value.ID)
   206  
   207  		v, err := recorder.Lookup.store.Get(k)
   208  		require.NoError(t, err)
   209  		require.NotEmpty(t, v)
   210  
   211  		var v1 mockInvitation
   212  		err = getAndUnmarshal(k, &v1, recorder.store)
   213  		require.NoError(t, err)
   214  		require.Equal(t, value, &v1)
   215  
   216  		var v2 mockInvitation
   217  		err = getAndUnmarshal(k, &v2, recorder.protocolStateStore)
   218  		require.Error(t, err)
   219  		require.Contains(t, err.Error(), "data not found")
   220  	})
   221  
   222  	t.Run("test save invitation failure due to invalid key", func(t *testing.T) {
   223  		store := &mockstorage.MockStore{Store: make(map[string]mockstorage.DBEntry)}
   224  		recorder, err := NewRecorder(&mockProvider{
   225  			store: store,
   226  		})
   227  		require.NoError(t, err)
   228  		require.NotNil(t, recorder)
   229  
   230  		value := &mockInvitation{
   231  			Label: "sample-label2",
   232  		}
   233  		err = recorder.SaveInvitation("", value)
   234  		require.Error(t, err)
   235  		require.Contains(t, err.Error(), "invalid key")
   236  	})
   237  }
   238  
   239  func TestConnectionStore_GetInvitation(t *testing.T) {
   240  	t.Run("test get invitation - success", func(t *testing.T) {
   241  		recorder, err := NewRecorder(&mockProvider{})
   242  		require.NoError(t, err)
   243  		require.NotNil(t, recorder)
   244  
   245  		valueStored := &mockInvitation{
   246  			ID:    "sample-id-3",
   247  			Label: "sample-label-3",
   248  		}
   249  
   250  		err = recorder.SaveInvitation(valueStored.ID, valueStored)
   251  		require.NoError(t, err)
   252  
   253  		var valueFound mockInvitation
   254  		err = recorder.GetInvitation(valueStored.ID, &valueFound)
   255  		require.NoError(t, err)
   256  		require.Equal(t, valueStored, &valueFound)
   257  	})
   258  
   259  	t.Run("test get invitation - not found scenario", func(t *testing.T) {
   260  		recorder, err := NewRecorder(&mockProvider{})
   261  		require.NoError(t, err)
   262  		require.NotNil(t, recorder)
   263  
   264  		var valueFound mockInvitation
   265  		err = recorder.GetInvitation("sample-key4", &valueFound)
   266  		require.Error(t, err)
   267  		require.Equal(t, err, storage.ErrDataNotFound)
   268  	})
   269  
   270  	t.Run("test get invitation - invalid key scenario", func(t *testing.T) {
   271  		recorder, err := NewRecorder(&mockProvider{})
   272  		require.NoError(t, err)
   273  		require.NotNil(t, recorder)
   274  
   275  		var valueFound mockInvitation
   276  		err = recorder.GetInvitation("", &valueFound)
   277  		require.Error(t, err)
   278  		require.Contains(t, err.Error(), errMsgInvalidKey)
   279  	})
   280  }
   281  
   282  func TestConnectionStore_SaveAndGetOOBv2Invitation(t *testing.T) {
   283  	t.Run("success", func(t *testing.T) {
   284  		recorder, err := NewRecorder(&mockProvider{})
   285  		require.NoError(t, err)
   286  		require.NotNil(t, recorder)
   287  
   288  		mockDID := "did:test:abc:def"
   289  
   290  		valueStored := &mockInvitation{
   291  			ID:    "sample-id-3",
   292  			Label: "sample-label-3",
   293  		}
   294  
   295  		err = recorder.SaveOOBv2Invitation(mockDID, valueStored)
   296  		require.NoError(t, err)
   297  
   298  		var valueFound mockInvitation
   299  		err = recorder.GetOOBv2Invitation(mockDID, &valueFound)
   300  		require.NoError(t, err)
   301  		require.Equal(t, valueStored, &valueFound)
   302  	})
   303  
   304  	t.Run("fail: lookup without key", func(t *testing.T) {
   305  		recorder, err := NewRecorder(&mockProvider{})
   306  		require.NoError(t, err)
   307  		require.NotNil(t, recorder)
   308  
   309  		err = recorder.GetOOBv2Invitation("", nil)
   310  		require.Error(t, err)
   311  		require.Contains(t, err.Error(), errMsgInvalidKey)
   312  	})
   313  }
   314  
   315  func TestConnectionStore_SaveAndGetEventData(t *testing.T) {
   316  	t.Run("test save and get event data - success", func(t *testing.T) {
   317  		recorder, err := NewRecorder(&mockProvider{})
   318  		require.NoError(t, err)
   319  		require.NotNil(t, recorder)
   320  
   321  		valueStored := []byte("sample-event-data")
   322  
   323  		err = recorder.SaveEvent(sampleConnID, valueStored)
   324  		require.NoError(t, err)
   325  
   326  		valueFound, err := recorder.GetEvent(sampleConnID)
   327  		require.NoError(t, err)
   328  		require.Equal(t, valueStored, valueFound)
   329  	})
   330  
   331  	t.Run("test get invitation - not found scenario", func(t *testing.T) {
   332  		recorder, err := NewRecorder(&mockProvider{})
   333  		require.NoError(t, err)
   334  		require.NotNil(t, recorder)
   335  
   336  		value, err := recorder.GetEvent(sampleConnID)
   337  		require.Error(t, err)
   338  		require.Equal(t, err, storage.ErrDataNotFound)
   339  		require.Nil(t, value)
   340  	})
   341  
   342  	t.Run("test get invitation - invalid key scenario", func(t *testing.T) {
   343  		recorder, err := NewRecorder(&mockProvider{})
   344  		require.NoError(t, err)
   345  		require.NotNil(t, recorder)
   346  
   347  		value, err := recorder.GetEvent("")
   348  		require.Error(t, err)
   349  		require.Contains(t, err.Error(), errMsgInvalidKey)
   350  		require.Nil(t, value)
   351  	})
   352  }
   353  
   354  func TestConnectionRecordByState(t *testing.T) {
   355  	recorder, err := NewRecorder(&mockProvider{})
   356  	require.NoError(t, err)
   357  
   358  	connRec := &Record{
   359  		ConnectionID: uuid.New().String(), ThreadID: threadIDValue,
   360  		Namespace: MyNSPrefix, State: "requested",
   361  	}
   362  	err = recorder.SaveConnectionRecord(connRec)
   363  	require.NoError(t, err)
   364  
   365  	// data exists
   366  	storedConnRec, err := recorder.GetConnectionRecordAtState(connRec.ConnectionID, "requested")
   367  	require.NoError(t, err)
   368  	require.Equal(t, storedConnRec, connRec)
   369  
   370  	// data doesn't exists
   371  	_, err = recorder.GetConnectionRecordAtState(connRec.ConnectionID, "invalid")
   372  	require.Error(t, err)
   373  	require.Contains(t, err.Error(), "data not found")
   374  
   375  	// data with no state details
   376  	connRec = &Record{
   377  		ConnectionID: uuid.New().String(), ThreadID: threadIDValue,
   378  		Namespace: MyNSPrefix,
   379  	}
   380  	err = recorder.SaveConnectionRecord(connRec)
   381  	require.NoError(t, err)
   382  	_, err = recorder.GetConnectionRecordAtState(connRec.ConnectionID, "requested")
   383  	require.Error(t, err)
   384  	require.Contains(t, err.Error(), "data not found")
   385  
   386  	// get with empty stateID
   387  	_, err = recorder.GetConnectionRecordAtState(connRec.ConnectionID, "")
   388  	require.Error(t, err)
   389  	require.Contains(t, err.Error(), "stateID can't be empty")
   390  }
   391  
   392  func TestConnectionRecorder_SaveConnectionRecord(t *testing.T) {
   393  	t.Run("save connection record with invited state - success", func(t *testing.T) {
   394  		recorder, err := NewRecorder(&mockProvider{})
   395  		require.NoError(t, err)
   396  		require.NotNil(t, recorder)
   397  
   398  		record := &Record{
   399  			ThreadID:     threadIDValue,
   400  			ConnectionID: uuid.New().String(), State: stateNameInvited, Namespace: TheirNSPrefix,
   401  		}
   402  		err = recorder.SaveConnectionRecord(record)
   403  		require.NoError(t, err)
   404  
   405  		recordFound, err := recorder.GetConnectionRecord(record.ConnectionID)
   406  		require.NoError(t, err)
   407  		require.NotNil(t, recordFound)
   408  		require.Equal(t, record, recordFound)
   409  
   410  		// make sure it exists only in protocol state store
   411  		var r1 Record
   412  		err = getAndUnmarshal(getConnectionKeyPrefix()(record.ConnectionID), &r1, recorder.store)
   413  		require.Error(t, err)
   414  		require.Contains(t, err.Error(), "data not found")
   415  
   416  		var r2 Record
   417  		err = getAndUnmarshal(getConnectionKeyPrefix()(record.ConnectionID), &r2, recorder.protocolStateStore)
   418  		require.NoError(t, err)
   419  		require.Equal(t, record, &r2)
   420  	})
   421  
   422  	t.Run("save connection record with invited state - completed", func(t *testing.T) {
   423  		recorder, err := NewRecorder(&mockProvider{})
   424  		require.NoError(t, err)
   425  		require.NotNil(t, recorder)
   426  
   427  		record := &Record{
   428  			ThreadID:     threadIDValue,
   429  			ConnectionID: uuid.New().String(),
   430  			State:        StateNameCompleted,
   431  			Namespace:    TheirNSPrefix,
   432  			MyDID:        "did:mydid:123",
   433  			TheirDID:     "did:theirdid:123",
   434  		}
   435  		err = recorder.SaveConnectionRecord(record)
   436  		require.NoError(t, err)
   437  
   438  		recordFound, err := recorder.GetConnectionRecord(record.ConnectionID)
   439  		require.NoError(t, err)
   440  		require.NotNil(t, recordFound)
   441  		require.Equal(t, record, recordFound)
   442  
   443  		// make sure it exists only in both permanent and protocol state store
   444  		var r1 Record
   445  		err = getAndUnmarshal(getConnectionKeyPrefix()(record.ConnectionID), &r1, recorder.protocolStateStore)
   446  		require.NoError(t, err)
   447  		require.Equal(t, record, &r1)
   448  
   449  		var r2 Record
   450  		err = getAndUnmarshal(getConnectionKeyPrefix()(record.ConnectionID), &r2, recorder.store)
   451  		require.NoError(t, err)
   452  		require.Equal(t, record, &r2)
   453  	})
   454  
   455  	t.Run("save connection record error scenario 1", func(t *testing.T) {
   456  		const errMsg = "get error"
   457  		record, err := NewRecorder(&mockProvider{
   458  			protocolStateStore: &mockstorage.MockStore{
   459  				Store:  make(map[string]mockstorage.DBEntry),
   460  				ErrPut: fmt.Errorf(errMsg),
   461  			},
   462  		})
   463  		require.NoError(t, err)
   464  		connRec := &Record{
   465  			ThreadID:     "",
   466  			ConnectionID: "test", State: stateNameInvited, Namespace: TheirNSPrefix,
   467  		}
   468  		err = record.SaveConnectionRecord(connRec)
   469  		require.Contains(t, err.Error(), errMsg)
   470  	})
   471  
   472  	t.Run("save connection record error scenario 2", func(t *testing.T) {
   473  		const errMsg = "get error"
   474  		record, err := NewRecorder(&mockProvider{
   475  			store: &mockstorage.MockStore{
   476  				Store:  make(map[string]mockstorage.DBEntry),
   477  				ErrPut: fmt.Errorf(errMsg),
   478  			},
   479  		})
   480  		require.NoError(t, err)
   481  		connRec := &Record{
   482  			ThreadID:     "",
   483  			ConnectionID: "test", State: StateNameCompleted, Namespace: TheirNSPrefix,
   484  		}
   485  		err = record.SaveConnectionRecord(connRec)
   486  		require.Contains(t, err.Error(), errMsg)
   487  	})
   488  }
   489  
   490  func TestConnectionRecorder_RemoveConnection(t *testing.T) {
   491  	t.Run("save and remove connection record with invited state - completed", func(t *testing.T) {
   492  		recorder, err := NewRecorder(&mockProvider{})
   493  		require.NoError(t, err)
   494  		require.NotNil(t, recorder)
   495  
   496  		record := &Record{
   497  			ThreadID:     threadIDValue,
   498  			ConnectionID: uuid.New().String(),
   499  			State:        StateNameCompleted,
   500  			Namespace:    TheirNSPrefix,
   501  			MyDID:        "did:mydid:123",
   502  			TheirDID:     "did:theirdid:123",
   503  		}
   504  		err = recorder.SaveConnectionRecord(record)
   505  		require.NoError(t, err)
   506  
   507  		recordFound, err := recorder.GetConnectionRecord(record.ConnectionID)
   508  		require.NoError(t, err)
   509  		require.NotNil(t, recordFound)
   510  		require.Equal(t, record, recordFound)
   511  
   512  		err = recorder.RemoveConnection(record.ConnectionID)
   513  		require.NoError(t, err)
   514  
   515  		// make sure no records exist in both permanent and protocol state store
   516  		var r1 Record
   517  		err = getAndUnmarshal(getConnectionKeyPrefix()(record.ConnectionID), &r1, recorder.protocolStateStore)
   518  		require.Error(t, err)
   519  		require.Contains(t, err.Error(), "data not found")
   520  
   521  		var r2 Record
   522  		err = getAndUnmarshal(getConnectionKeyPrefix()(record.ConnectionID), &r2, recorder.store)
   523  		require.Error(t, err)
   524  		require.Contains(t, err.Error(), "data not found")
   525  
   526  		itr, err := recorder.protocolStateStore.Query(connStateKeyPrefix)
   527  		require.NoError(t, err)
   528  
   529  		hasRecords, err := itr.Next()
   530  		require.NoError(t, err)
   531  
   532  		if hasRecords {
   533  			key, errKey := itr.Key()
   534  			require.NoError(t, errKey)
   535  
   536  			t.Errorf("protocol state store still has connection state records: key=%s", key)
   537  		}
   538  
   539  		_, err = recorder.GetConnectionRecordByDIDs(record.MyDID, record.TheirDID)
   540  		require.Error(t, err)
   541  		require.Contains(t, err.Error(), "data not found")
   542  	})
   543  	t.Run("try to remove unexisting connection record", func(t *testing.T) {
   544  		recorder, err := NewRecorder(&mockProvider{})
   545  		require.NoError(t, err)
   546  		require.NotNil(t, recorder)
   547  
   548  		record := &Record{
   549  			ThreadID:     threadIDValue,
   550  			ConnectionID: uuid.New().String(),
   551  			State:        StateNameCompleted,
   552  			Namespace:    TheirNSPrefix,
   553  			MyDID:        "did:mydid:123",
   554  			TheirDID:     "did:theirdid:123",
   555  		}
   556  
   557  		err = recorder.RemoveConnection(record.ConnectionID)
   558  		require.Error(t, err)
   559  		require.Contains(t, err.Error(), "data not found")
   560  	})
   561  	t.Run("save and remove connection record - failed to delete from the store", func(t *testing.T) {
   562  		const errMsg = "get error"
   563  		recorder, err := NewRecorder(&mockProvider{
   564  			store: &mockstorage.MockStore{
   565  				Store:     make(map[string]mockstorage.DBEntry),
   566  				ErrDelete: fmt.Errorf(errMsg),
   567  			},
   568  		})
   569  		require.NoError(t, err)
   570  		require.NotNil(t, recorder)
   571  
   572  		record := &Record{
   573  			ThreadID:     threadIDValue,
   574  			ConnectionID: uuid.New().String(),
   575  			State:        StateNameCompleted,
   576  			Namespace:    TheirNSPrefix,
   577  			MyDID:        "did:mydid:123",
   578  			TheirDID:     "did:theirdid:123",
   579  		}
   580  		err = recorder.SaveConnectionRecord(record)
   581  		require.NoError(t, err)
   582  
   583  		err = recorder.RemoveConnection(record.ConnectionID)
   584  		require.Error(t, err)
   585  		require.Contains(t, err.Error(), errMsg)
   586  	})
   587  	t.Run("save and remove connection record - failed to delete from the protocol state store", func(t *testing.T) {
   588  		const errMsg = "get error"
   589  		recorder, err := NewRecorder(&mockProvider{
   590  			protocolStateStore: &mockstorage.MockStore{
   591  				Store:     make(map[string]mockstorage.DBEntry),
   592  				ErrDelete: fmt.Errorf(errMsg),
   593  			},
   594  		})
   595  		require.NoError(t, err)
   596  		require.NotNil(t, recorder)
   597  
   598  		record := &Record{
   599  			ThreadID:     threadIDValue,
   600  			ConnectionID: uuid.New().String(),
   601  			State:        StateNameCompleted,
   602  			Namespace:    TheirNSPrefix,
   603  			MyDID:        "did:mydid:123",
   604  			TheirDID:     "did:theirdid:123",
   605  		}
   606  		err = recorder.SaveConnectionRecord(record)
   607  		require.NoError(t, err)
   608  
   609  		err = recorder.RemoveConnection(record.ConnectionID)
   610  		require.Error(t, err)
   611  		require.Contains(t, err.Error(), errMsg)
   612  	})
   613  	t.Run("save and remove connection record - failed to delete connection mapping record", func(t *testing.T) {
   614  		recorder, err := NewRecorder(&mockProvider{})
   615  		require.NoError(t, err)
   616  		require.NotNil(t, recorder)
   617  
   618  		record := &Record{
   619  			ThreadID:     "",
   620  			ConnectionID: uuid.New().String(),
   621  			State:        StateNameCompleted,
   622  			Namespace:    TheirNSPrefix,
   623  			MyDID:        "did:mydid:123",
   624  			TheirDID:     "did:theirdid:123",
   625  		}
   626  		err = recorder.SaveConnectionRecord(record)
   627  		require.NoError(t, err)
   628  
   629  		err = recorder.RemoveConnection(record.ConnectionID)
   630  		require.Error(t, err)
   631  		require.Contains(t, err.Error(), "empty bytes")
   632  	})
   633  }
   634  
   635  func TestConnectionRecorder_ConnectionRecordMappings(t *testing.T) {
   636  	t.Run("get connection record by namespace threadID in my namespace", func(t *testing.T) {
   637  		recorder, err := NewRecorder(&mockProvider{})
   638  		require.NoError(t, err)
   639  
   640  		require.NotNil(t, recorder)
   641  		connRec := &Record{
   642  			ThreadID:     threadIDValue,
   643  			ConnectionID: sampleConnID, State: stateNameInvited, Namespace: MyNSPrefix,
   644  		}
   645  		err = recorder.SaveConnectionRecordWithMappings(connRec)
   646  		require.NoError(t, err)
   647  
   648  		nsThreadID, err := CreateNamespaceKey(MyNSPrefix, threadIDValue)
   649  		require.NoError(t, err)
   650  
   651  		storedRecord, err := recorder.GetConnectionRecordByNSThreadID(nsThreadID)
   652  		require.NoError(t, err)
   653  		require.Equal(t, connRec, storedRecord)
   654  	})
   655  	t.Run("get connection record by namespace threadID their namespace", func(t *testing.T) {
   656  		recorder, err := NewRecorder(&mockProvider{})
   657  		require.NoError(t, err)
   658  		require.NotNil(t, recorder)
   659  		connRec := &Record{
   660  			ThreadID:     threadIDValue,
   661  			ConnectionID: sampleConnID, State: stateNameInvited, Namespace: TheirNSPrefix,
   662  		}
   663  		err = recorder.SaveConnectionRecordWithMappings(connRec)
   664  		require.NoError(t, err)
   665  
   666  		nsThreadID, err := CreateNamespaceKey(TheirNSPrefix, threadIDValue)
   667  		require.NoError(t, err)
   668  
   669  		storedRecord, err := recorder.GetConnectionRecordByNSThreadID(nsThreadID)
   670  		require.NoError(t, err)
   671  		require.Equal(t, connRec, storedRecord)
   672  	})
   673  	t.Run("save connection record with mapping - validation failure", func(t *testing.T) {
   674  		recorder, err := NewRecorder(&mockProvider{})
   675  		require.NoError(t, err)
   676  
   677  		require.NotNil(t, recorder)
   678  		connRec := &Record{
   679  			ThreadID:     "",
   680  			ConnectionID: sampleConnID, State: stateNameInvited, Namespace: MyNSPrefix,
   681  		}
   682  		err = recorder.SaveConnectionRecordWithMappings(connRec)
   683  		require.Error(t, err)
   684  		require.Contains(t, err.Error(), "validation failed")
   685  	})
   686  	t.Run("save connection record with mapping - store failure", func(t *testing.T) {
   687  		const errMsg = "put error"
   688  		recorder, err := NewRecorder(&mockProvider{
   689  			protocolStateStore: &mockstorage.MockStore{
   690  				Store:  make(map[string]mockstorage.DBEntry),
   691  				ErrPut: fmt.Errorf(errMsg),
   692  			},
   693  		})
   694  
   695  		require.NotNil(t, recorder)
   696  		require.NoError(t, err)
   697  
   698  		connRec := &Record{
   699  			ThreadID:     threadIDValue,
   700  			ConnectionID: sampleConnID, State: stateNameInvited, Namespace: MyNSPrefix,
   701  		}
   702  		err = recorder.SaveConnectionRecordWithMappings(connRec)
   703  		require.Error(t, err)
   704  		require.Contains(t, err.Error(), errMsg)
   705  	})
   706  	t.Run("save connection record with mapping - namespace error", func(t *testing.T) {
   707  		recorder, err := NewRecorder(&mockProvider{})
   708  		require.NoError(t, err)
   709  
   710  		require.NotNil(t, recorder)
   711  		connRec := &Record{
   712  			ThreadID:     threadIDValue,
   713  			ConnectionID: sampleConnID, State: stateNameInvited, Namespace: "invalid-ns",
   714  		}
   715  		err = recorder.SaveConnectionRecordWithMappings(connRec)
   716  		require.Error(t, err)
   717  		require.Contains(t, err.Error(), "namespace not supported")
   718  	})
   719  	t.Run("data not found error due to missing input parameter", func(t *testing.T) {
   720  		recorder, err := NewRecorder(&mockProvider{})
   721  		require.NoError(t, err)
   722  		require.NotNil(t, recorder)
   723  		connRec, err := recorder.GetConnectionRecordByNSThreadID("")
   724  		require.Contains(t, err.Error(), "data not found")
   725  		require.Nil(t, connRec)
   726  	})
   727  }
   728  
   729  func TestConnectionRecorder_CreateNSKeys(t *testing.T) {
   730  	t.Run("creating their namespace key success", func(t *testing.T) {
   731  		key, err := CreateNamespaceKey(TheirNSPrefix, threadIDValue)
   732  		require.NoError(t, err)
   733  		require.NotNil(t, key)
   734  	})
   735  	t.Run("check error while creating my namespace key", func(t *testing.T) {
   736  		_, err := CreateNamespaceKey(MyNSPrefix, "")
   737  		require.Contains(t, err.Error(), "empty bytes")
   738  	})
   739  }
   740  
   741  func TestConnectionRecorder_SaveNamespaceThreadID(t *testing.T) {
   742  	t.Run("missing required parameters", func(t *testing.T) {
   743  		recorder, err := NewRecorder(&mockProvider{})
   744  		require.NoError(t, err)
   745  
   746  		require.NotNil(t, recorder)
   747  		err = recorder.SaveNamespaceThreadID("", TheirNSPrefix, sampleConnID)
   748  		require.Error(t, err)
   749  		err = recorder.SaveNamespaceThreadID("", MyNSPrefix, sampleConnID)
   750  		require.Error(t, err)
   751  		err = recorder.SaveNamespaceThreadID(threadIDValue, "", sampleConnID)
   752  		require.Error(t, err)
   753  	})
   754  }
   755  
   756  func TestConnectionRecorder_SaveAndGet(t *testing.T) {
   757  	const noOfRecords = 12
   758  
   759  	records := make([]*mockInvitation, noOfRecords)
   760  	for i := 0; i < noOfRecords; i++ {
   761  		records[i] = &mockInvitation{ID: fmt.Sprintf("conn-%d", i)}
   762  	}
   763  
   764  	t.Run("save and get in store - success", func(t *testing.T) {
   765  		require.NotEmpty(t, records)
   766  		store := &mockstorage.MockStore{Store: make(map[string]mockstorage.DBEntry)}
   767  
   768  		for _, record := range records {
   769  			err := marshalAndSave(record.ID, record, store)
   770  			require.NoError(t, err)
   771  		}
   772  
   773  		for _, record := range records {
   774  			var recordFound1 mockInvitation
   775  			err := getAndUnmarshal(record.ID, &recordFound1, store)
   776  			require.NoError(t, err)
   777  			require.Equal(t, record, &recordFound1)
   778  		}
   779  	})
   780  
   781  	t.Run("save and get in store - store failure", func(t *testing.T) {
   782  		const errMsg = "put error"
   783  
   784  		store := &mockstorage.MockStore{
   785  			Store:  make(map[string]mockstorage.DBEntry),
   786  			ErrPut: fmt.Errorf(errMsg),
   787  			ErrGet: fmt.Errorf(errMsg),
   788  		}
   789  
   790  		require.NotEmpty(t, records)
   791  
   792  		for _, record := range records {
   793  			err := marshalAndSave(record.ID, record, store)
   794  			require.Error(t, err)
   795  			require.Contains(t, err.Error(), errMsg)
   796  		}
   797  
   798  		for _, record := range records {
   799  			var recordFound1 mockInvitation
   800  			err := getAndUnmarshal(record.ID, &recordFound1, store)
   801  			require.Error(t, err)
   802  			require.Contains(t, err.Error(), errMsg)
   803  		}
   804  	})
   805  
   806  	t.Run("save and get in store - failure", func(t *testing.T) {
   807  		store := &mockstorage.MockStore{Store: make(map[string]mockstorage.DBEntry)}
   808  
   809  		err := marshalAndSave("sample-id", make(chan int), store)
   810  		require.Error(t, err)
   811  
   812  		err = marshalAndSave("sample-id", []byte("XYZ"), store)
   813  		require.NoError(t, err)
   814  
   815  		err = getAndUnmarshal("sample-id", make(chan int), store)
   816  		require.Error(t, err)
   817  	})
   818  }
   819  
   820  type mockInvitation struct {
   821  	ImageURL        string            `json:"imageUrl,omitempty"`
   822  	ServiceEndpoint string            `json:"serviceEndpoint,omitempty"`
   823  	RecipientKeys   []string          `json:"recipientKeys,omitempty"`
   824  	ID              string            `json:"@id,omitempty"`
   825  	Label           string            `json:"label,omitempty"`
   826  	DID             string            `json:"did,omitempty"`
   827  	RoutingKeys     []string          `json:"routingKeys,omitempty"`
   828  	Type            string            `json:"@type,omitempty"`
   829  	Thread          *decorator.Thread `json:"~thread,omitempty"`
   830  }